From c4919e56e5c130c459457ceb497057fabf9559cf Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Sun, 6 Jun 2004 21:44:48 -0700 Subject: [PATCH] ia64: delete McKinley A-stepping code The patch below removes the config option and one bit of code for McKinley A0/A1 CPU workarounds. These CPUs were never used in production, and IIRC this workaround really only affected X11. Signed-off-by: David Mosberger --- include/asm-ia64/pgtable.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'include') diff --git a/include/asm-ia64/pgtable.h b/include/asm-ia64/pgtable.h index 590432c08407..93d19ae78da2 100644 --- a/include/asm-ia64/pgtable.h +++ b/include/asm-ia64/pgtable.h @@ -297,11 +297,7 @@ ia64_phys_addr_valid (unsigned long addr) * works bypasses the caches, but does allow for consecutive writes to * be combined into single (but larger) write transactions. */ -#ifdef CONFIG_MCKINLEY_A0_SPECIFIC -# define pgprot_writecombine(prot) prot -#else -# define pgprot_writecombine(prot) __pgprot((pgprot_val(prot) & ~_PAGE_MA_MASK) | _PAGE_MA_WC) -#endif +#define pgprot_writecombine(prot) __pgprot((pgprot_val(prot) & ~_PAGE_MA_MASK) | _PAGE_MA_WC) static inline unsigned long pgd_index (unsigned long address) -- cgit v1.2.3 From 972d8c376ba5f72697cfa81ea2280af249b59d74 Mon Sep 17 00:00:00 2001 From: Arun Sharma Date: Mon, 7 Jun 2004 00:39:16 -0700 Subject: [PATCH] ia64: fix ia32 virtual memory leaks due to partial-page mappings Certain IA-32 applications which do mmap/munmaps which are not PAGE_SIZE aligned could see temporary (recovered at process exit time) memory leaks, because the kernel didn't have enough data to decide if the complete page could be unmapped. This patch adds a new data structure called the "partial page list" which helps the kernel keep track of precisely which 4k pages are in use by the IA-32 application. Armed with this data, the kernel can make better decisions at munmap and mprotect time. No significant performance degradation was observed in the workloads we tested and in some cases, the performance actually improved! This is possibly due to the reduced length of the vma list. Signed-off-by: Arun Sharma Signed-off-by: Gordon Jin Signed-off-by: David Mosberger --- arch/ia64/ia32/binfmt_elf32.c | 4 + arch/ia64/ia32/ia32_entry.S | 2 +- arch/ia64/ia32/ia32_support.c | 8 + arch/ia64/ia32/ia32priv.h | 25 ++ arch/ia64/ia32/sys_ia32.c | 529 +++++++++++++++++++++++++++++++++++++++++- arch/ia64/kernel/process.c | 12 + include/asm-ia64/ia32.h | 2 + include/asm-ia64/processor.h | 5 +- 8 files changed, 580 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/arch/ia64/ia32/binfmt_elf32.c b/arch/ia64/ia32/binfmt_elf32.c index babf3d1fcf40..1726c6114d81 100644 --- a/arch/ia64/ia32/binfmt_elf32.c +++ b/arch/ia64/ia32/binfmt_elf32.c @@ -197,6 +197,10 @@ ia32_setup_arg_pages (struct linux_binprm *bprm, int executable_stack) } up_write(¤t->mm->mmap_sem); + /* Can't do it in ia64_elf32_init(). Needs to be done before calls to + elf32_map() */ + current->thread.ppl = ia32_init_pp_list(); + return 0; } diff --git a/arch/ia64/ia32/ia32_entry.S b/arch/ia64/ia32/ia32_entry.S index 4ee1e551f569..3b9fc4d084c6 100644 --- a/arch/ia64/ia32/ia32_entry.S +++ b/arch/ia64/ia32/ia32_entry.S @@ -371,7 +371,7 @@ ia32_syscall_table: data8 sys_sched_get_priority_min /* 160 */ data8 sys32_sched_rr_get_interval data8 compat_sys_nanosleep - data8 sys_mremap + data8 sys32_mremap data8 sys_setresuid /* 16-bit version */ data8 sys32_getresuid16 /* 16-bit version */ /* 165 */ data8 sys_ni_syscall /* vm86 */ diff --git a/arch/ia64/ia32/ia32_support.c b/arch/ia64/ia32/ia32_support.c index c56fd9f3c5c8..e1da7f8bd0c5 100644 --- a/arch/ia64/ia32/ia32_support.c +++ b/arch/ia64/ia32/ia32_support.c @@ -211,6 +211,8 @@ ia32_cpu_init (void) static int __init ia32_init (void) { + extern kmem_cache_t *partial_page_cachep; + ia32_exec_domain.name = "Linux/x86"; ia32_exec_domain.handler = NULL; ia32_exec_domain.pers_low = PER_LINUX32; @@ -218,6 +220,12 @@ ia32_init (void) ia32_exec_domain.signal_map = default_exec_domain.signal_map; ia32_exec_domain.signal_invmap = default_exec_domain.signal_invmap; register_exec_domain(&ia32_exec_domain); + + partial_page_cachep = kmem_cache_create("partial_page_cache", + sizeof(struct partial_page), 0, 0, NULL, NULL); + if (!partial_page_cachep) + panic("Cannot create partial page SLAB cache"); + return 0; } diff --git a/arch/ia64/ia32/ia32priv.h b/arch/ia64/ia32/ia32priv.h index e6f95af15972..8ffd9243248e 100644 --- a/arch/ia64/ia32/ia32priv.h +++ b/arch/ia64/ia32/ia32priv.h @@ -9,6 +9,7 @@ #include #include +#include #include @@ -22,6 +23,30 @@ #define IA32_PAGE_ALIGN(addr) (((addr) + IA32_PAGE_SIZE - 1) & IA32_PAGE_MASK) #define IA32_CLOCKS_PER_SEC 100 /* Cast in stone for IA32 Linux */ +/* + * partially mapped pages provide precise accounting of which 4k sub pages + * are mapped and which ones are not, thereby improving IA-32 compatibility. + */ +struct partial_page { + struct partial_page *next; /* linked list, sorted by address */ + struct rb_node pp_rb; + /* 64K is the largest "normal" page supported by ia64 ABI. So 4K*32 + * should suffice.*/ + unsigned int bitmap; + unsigned int base; +}; + +struct partial_page_list { + struct partial_page *pp_head; /* list head, points to the lowest + * addressed partial page */ + struct rb_root ppl_rb; + struct partial_page *pp_hint; /* pp_hint->next is the last + * accessed partial page */ + atomic_t pp_count; /* reference count */ +}; + +struct partial_page_list* ia32_init_pp_list (void); + /* sigcontext.h */ /* * As documented in the iBCS2 standard.. diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index 8994c9d11a55..dcfb337ab72b 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -8,6 +8,7 @@ * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 2000-2003 Hewlett-Packard Co * David Mosberger-Tang + * Copyright (C) 2004 Gordon Jin * * These routines maintain argument size conversion between 32bit and 64bit * environment. @@ -48,6 +49,7 @@ #include #include #include +#include #include #include @@ -250,6 +252,374 @@ mmap_subpage (struct file *file, unsigned long start, unsigned long end, int pro return ret; } +/* SLAB cache for partial_page structures */ +kmem_cache_t *partial_page_cachep; + +/* + * init partial_page_list. + * return 0 means kmalloc fail. + */ +struct partial_page_list* +ia32_init_pp_list(void) +{ + struct partial_page_list *p; + + if ((p = kmalloc(sizeof(*p), GFP_KERNEL)) == NULL) + return p; + p->pp_head = 0; + p->ppl_rb = RB_ROOT; + p->pp_hint = 0; + atomic_set(&p->pp_count, 1); + return p; +} + +/* + * Search for the partial page with @start in partial page list @ppl. + * If finds the partial page, return the found partial page. + * Else, return 0 and provide @pprev, @rb_link, @rb_parent to + * be used by later ia32_insert_pp(). + */ +static struct partial_page * +ia32_find_pp(struct partial_page_list *ppl, unsigned int start, + struct partial_page **pprev, struct rb_node ***rb_link, + struct rb_node **rb_parent) +{ + struct partial_page *pp; + struct rb_node **__rb_link, *__rb_parent, *rb_prev; + + pp = ppl->pp_hint; + if (pp && pp->base == start) + return pp; + + __rb_link = &ppl->ppl_rb.rb_node; + rb_prev = __rb_parent = NULL; + + while (*__rb_link) { + __rb_parent = *__rb_link; + pp = rb_entry(__rb_parent, struct partial_page, pp_rb); + + if (pp->base == start) { + ppl->pp_hint = pp; + return pp; + } else if (pp->base < start) { + rb_prev = __rb_parent; + __rb_link = &__rb_parent->rb_right; + } else { + __rb_link = &__rb_parent->rb_left; + } + } + + *rb_link = __rb_link; + *rb_parent = __rb_parent; + *pprev = NULL; + if (rb_prev) + *pprev = rb_entry(rb_prev, struct partial_page, pp_rb); + return NULL; +} + +/* + * insert @pp into @ppl. + */ +static void +ia32_insert_pp(struct partial_page_list *ppl, struct partial_page *pp, + struct partial_page *prev, struct rb_node **rb_link, + struct rb_node *rb_parent) +{ + /* link list */ + if (prev) { + pp->next = prev->next; + prev->next = pp; + } else { + ppl->pp_head = pp; + if (rb_parent) + pp->next = rb_entry(rb_parent, + struct partial_page, pp_rb); + else + pp->next = NULL; + } + + /* link rb */ + rb_link_node(&pp->pp_rb, rb_parent, rb_link); + rb_insert_color(&pp->pp_rb, &ppl->ppl_rb); + + ppl->pp_hint = pp; +} + +/* + * delete @pp from partial page list @ppl. + */ +static void +ia32_delete_pp(struct partial_page_list *ppl, struct partial_page *pp, + struct partial_page *prev) +{ + if (prev) { + prev->next = pp->next; + if (ppl->pp_hint == pp) + ppl->pp_hint = prev; + } else { + ppl->pp_head = pp->next; + if (ppl->pp_hint == pp) + ppl->pp_hint = pp->next; + } + rb_erase(&pp->pp_rb, &ppl->ppl_rb); + kmem_cache_free(partial_page_cachep, pp); +} + +static struct partial_page * +pp_prev(struct partial_page *pp) +{ + struct rb_node *prev = rb_prev(&pp->pp_rb); + if (prev) + return rb_entry(prev, struct partial_page, pp_rb); + else + return NULL; +} + +/* + * Set the range between @start and @end in bitmap. + * @start and @end should be IA32 page aligned and in the same IA64 page. + */ +static int +__ia32_set_pp(unsigned int start, unsigned int end) +{ + struct partial_page *pp, *prev; + struct rb_node ** rb_link, *rb_parent; + unsigned int pstart, start_bit, end_bit, i; + + pstart = PAGE_START(start); + start_bit = (start % PAGE_SIZE) / IA32_PAGE_SIZE; + end_bit = (end % PAGE_SIZE) / IA32_PAGE_SIZE; + if (end_bit == 0) + end_bit = PAGE_SIZE / IA32_PAGE_SIZE; + pp = ia32_find_pp(current->thread.ppl, pstart, &prev, + &rb_link, &rb_parent); + if (pp) { + for (i = start_bit; i < end_bit; i++) + set_bit(i, &pp->bitmap); + /* + * Check: if this partial page has been set to a full page, + * then delete it. + */ + if (find_first_zero_bit(&pp->bitmap, sizeof(pp->bitmap)*8) >= + PAGE_SIZE/IA32_PAGE_SIZE) { + ia32_delete_pp(current->thread.ppl, pp, pp_prev(pp)); + } + return 0; + } + + /* new a partial_page */ + pp = kmem_cache_alloc(partial_page_cachep, GFP_KERNEL); + if (!pp) + return -ENOMEM; + pp->base = pstart; + pp->bitmap = 0; + for (i=start_bit; ibitmap)); + pp->next = NULL; + ia32_insert_pp(current->thread.ppl, pp, prev, rb_link, rb_parent); + return 0; +} + +/* + * locking version of ia32_set_pp(). + * Use mm->mmap_sem to protect partial_page_list. + */ +static void +ia32_set_pp(unsigned int start, unsigned int end) +{ + down_write(¤t->mm->mmap_sem); + { + __ia32_set_pp(start, end); + } + up_write(¤t->mm->mmap_sem); +} + +/* + * Unset the range between @start and @end in bitmap. + * @start and @end should be IA32 page aligned and in the same IA64 page. + * After doing that, if the bitmap is 0, then free the page and return 1, + * else return 0; + * If not find the partial page in the list, then + * If the vma exists, then the full page is set to a partial page; + * Else return -ENOMEM. + */ +static int +__ia32_unset_pp(unsigned int start, unsigned int end) +{ + struct partial_page *pp, *prev; + struct rb_node ** rb_link, *rb_parent; + unsigned int pstart, start_bit, end_bit, i; + struct vm_area_struct *vma; + + pstart = PAGE_START(start); + start_bit = (start % PAGE_SIZE) / IA32_PAGE_SIZE; + end_bit = (end % PAGE_SIZE) / IA32_PAGE_SIZE; + if (end_bit == 0) + end_bit = PAGE_SIZE / IA32_PAGE_SIZE; + + pp = ia32_find_pp(current->thread.ppl, pstart, &prev, + &rb_link, &rb_parent); + if (pp) { + for (i = start_bit; i < end_bit; i++) + clear_bit(i, &pp->bitmap); + if (pp->bitmap == 0) { + ia32_delete_pp(current->thread.ppl, pp, pp_prev(pp)); + return 1; + } + return 0; + } + + vma = find_vma(current->mm, pstart); + if (!vma || vma->vm_start > pstart) { + return -ENOMEM; + } + + /* new a partial_page */ + pp = kmem_cache_alloc(partial_page_cachep, GFP_KERNEL); + if (!pp) + return -ENOMEM; + pp->base = pstart; + pp->bitmap = 0; + for (i = 0; i < start_bit; i++) + set_bit(i, &(pp->bitmap)); + for (i = end_bit; i < PAGE_SIZE / IA32_PAGE_SIZE; i++) + set_bit(i, &(pp->bitmap)); + pp->next = NULL; + ia32_insert_pp(current->thread.ppl, pp, prev, rb_link, rb_parent); + return 0; +} + +/* + * locking version of ia32_unset_pp(). + * Use mm->mmap_sem to protect partial_page_list. + */ +static int +ia32_unset_pp(unsigned int start, unsigned int end) +{ + int ret; + + down_write(¤t->mm->mmap_sem); + { + ret = __ia32_unset_pp(start, end); + } + up_write(¤t->mm->mmap_sem); + + return ret; +} + +/* + * Compare the range between @start and @end with bitmap in partial page: + * Take this as example: the range is the 1st and 2nd 4K page. + * Return 0 if they fit bitmap exactly, i.e. bitmap = 00000011; + * Return 1 if the range doesn't cover whole bitmap, e.g. bitmap = 00001111; + * Return -ENOMEM if the range exceeds the bitmap, e.g. bitmap = 00000001 or + * bitmap = 00000101. + */ +static int +ia32_compare_pp(unsigned int start, unsigned int end) +{ + struct partial_page *pp, *prev; + struct rb_node ** rb_link, *rb_parent; + unsigned int pstart, start_bit, end_bit, size; + unsigned int first_bit, next_zero_bit; /* the first range in bitmap */ + + pstart = PAGE_START(start); + + pp = ia32_find_pp(current->thread.ppl, pstart, &prev, + &rb_link, &rb_parent); + if (!pp) + return 1; + + start_bit = (start % PAGE_SIZE) / IA32_PAGE_SIZE; + end_bit = (end % PAGE_SIZE) / IA32_PAGE_SIZE; + size = sizeof(pp->bitmap) * 8; + first_bit = find_first_bit(&pp->bitmap, size); + next_zero_bit = find_next_zero_bit(&pp->bitmap, size, first_bit); + if ((start_bit < first_bit) || (end_bit > next_zero_bit)) { + /* exceeds the first range in bitmap */ + return -ENOMEM; + } else if ((start_bit == first_bit) && (end_bit == next_zero_bit)) { + first_bit = find_next_bit(&pp->bitmap, size, next_zero_bit); + if ((next_zero_bit < first_bit) && (first_bit < size)) + return 1; /* has next range */ + else + return 0; /* no next range */ + } else + return 1; +} + +static void +ia32_do_drop_pp_list(struct partial_page_list *ppl) +{ + struct partial_page *pp = ppl->pp_head; + + while (pp) { + struct partial_page *next = pp->next; + kmem_cache_free(partial_page_cachep, pp); + pp = next; + } + + kfree(ppl); +} + +void +ia32_drop_partial_page_list(struct partial_page_list* ppl) +{ + if (ppl && atomic_dec_and_test(&ppl->pp_count)) + ia32_do_drop_pp_list(ppl); +} + +/* + * Copy current->thread.ppl to ppl (already initialized). + */ +static int +ia32_do_copy_pp_list(struct partial_page_list *ppl) +{ + struct partial_page *pp, *tmp, *prev; + struct rb_node **rb_link, *rb_parent; + + ppl->pp_head = NULL; + ppl->pp_hint = NULL; + ppl->ppl_rb = RB_ROOT; + rb_link = &ppl->ppl_rb.rb_node; + rb_parent = NULL; + prev = NULL; + + for (pp = current->thread.ppl->pp_head; pp; pp = pp->next) { + tmp = kmem_cache_alloc(partial_page_cachep, GFP_KERNEL); + if (!tmp) + return -ENOMEM; + *tmp = *pp; + ia32_insert_pp(ppl, tmp, prev, rb_link, rb_parent); + prev = tmp; + rb_link = &tmp->pp_rb.rb_right; + rb_parent = &tmp->pp_rb; + } + return 0; +} + +int +ia32_copy_partial_page_list(struct task_struct *p, unsigned long clone_flags) +{ + int retval = 0; + + if (clone_flags & CLONE_VM) { + atomic_inc(¤t->thread.ppl->pp_count); + p->thread.ppl = current->thread.ppl; + } else { + p->thread.ppl = ia32_init_pp_list(); + if (!p->thread.ppl) + return -ENOMEM; + down_write(¤t->mm->mmap_sem); + { + retval = ia32_do_copy_pp_list(p->thread.ppl); + } + up_write(¤t->mm->mmap_sem); + } + + return retval; +} + static unsigned long emulate_mmap (struct file *file, unsigned long start, unsigned long len, int prot, int flags, loff_t off) @@ -274,7 +644,7 @@ emulate_mmap (struct file *file, unsigned long start, unsigned long len, int pro return ret; pstart += PAGE_SIZE; if (pstart >= pend) - return start; /* done */ + goto out; /* done */ } if (end < pend) { if (flags & MAP_SHARED) @@ -287,7 +657,7 @@ emulate_mmap (struct file *file, unsigned long start, unsigned long len, int pro return ret; pend -= PAGE_SIZE; if (pstart >= pend) - return start; /* done */ + goto out; /* done */ } } else { /* @@ -341,6 +711,19 @@ emulate_mmap (struct file *file, unsigned long start, unsigned long len, int pro if (!(prot & PROT_WRITE) && sys_mprotect(pstart, pend - pstart, prot) < 0) return -EINVAL; } + +out: + if (end < PAGE_ALIGN(start)) { + ia32_set_pp((unsigned int)start, (unsigned int)end); + } else { + if (start > PAGE_START(start)) { + ia32_set_pp((unsigned int)start, (unsigned int)PAGE_ALIGN(start)); + } + if (end < PAGE_ALIGN(end)) { + ia32_set_pp((unsigned int)PAGE_START(end), (unsigned int)end); + } + } + return start; } @@ -472,17 +855,51 @@ sys32_mmap2 (unsigned int addr, unsigned int len, unsigned int prot, unsigned in asmlinkage long sys32_munmap (unsigned int start, unsigned int len) { - unsigned int end = start + len; + unsigned int end, pstart, pend; long ret; + end = start + len; + #if PAGE_SHIFT <= IA32_PAGE_SHIFT ret = sys_munmap(start, end - start); #else + if (OFFSET4K(start)) + return -EINVAL; + + end = IA32_PAGE_ALIGN(end); if (start >= end) return -EINVAL; - start = PAGE_ALIGN(start); - end = PAGE_START(end); + pstart = PAGE_ALIGN(start); + pend = PAGE_START(end); + + if (end < PAGE_ALIGN(start)) { + ret = ia32_unset_pp((unsigned int)start, (unsigned int)end); + if (ret == 1) { + start = PAGE_START(start); + end = PAGE_ALIGN(end); + } else + return ret; + } else { + if (offset_in_page(start)) { + ret = ia32_unset_pp((unsigned int)start, (unsigned int)PAGE_ALIGN(start)); + if (ret == 1) + start = PAGE_START(start); + else if (ret == 0) + start = PAGE_ALIGN(start); + else + return ret; + } + if (offset_in_page(end)) { + ret = ia32_unset_pp((unsigned int)PAGE_START(end), (unsigned int)end); + if (ret == 1) + end = PAGE_ALIGN(end); + else if (ret == 0) + end = PAGE_START(end); + else + return ret; + } + } if (start >= end) return 0; @@ -540,6 +957,51 @@ sys32_mprotect (unsigned int start, unsigned int len, int prot) down(&ia32_mmap_sem); { + if (end < PAGE_ALIGN(start)) { + /* start and end are in the same IA64 (partial) page */ + retval = ia32_compare_pp((unsigned int)start, (unsigned int)end); + if (retval < 0) { + /* start~end beyond the mapped area */ + goto out; + } + else if (retval == 0) { + /* start~end fits the mapped area well */ + start = PAGE_START(start); + end = PAGE_ALIGN(end); + } + else { + /* start~end doesn't cover all mapped area in this IA64 page */ + /* So call mprotect_subpage to deal with new prot issue */ + goto subpage; + } + } else { + if (offset_in_page(start)) { + retval = ia32_compare_pp((unsigned int)start, (unsigned int)PAGE_ALIGN(start)); + if (retval < 0) { + goto out; + } + else if (retval == 0) { + start = PAGE_START(start); + } + else { + goto subpage; + } + } + if (offset_in_page(end)) { + retval = ia32_compare_pp((unsigned int)PAGE_START(end), (unsigned int)end); + if (retval < 0) { + goto out; + } + else if (retval == 0) { + end = PAGE_ALIGN(end); + } + else { + goto subpage; + } + } + } + + subpage: if (offset_in_page(start)) { /* start address is 4KB aligned but not page aligned. */ retval = mprotect_subpage(PAGE_START(start), prot); @@ -567,6 +1029,63 @@ sys32_mprotect (unsigned int start, unsigned int len, int prot) #endif } +asmlinkage long +sys32_mremap (unsigned int addr, unsigned int old_len, unsigned int new_len, + unsigned int flags, unsigned int new_addr) +{ + long ret; + unsigned int old_end, new_end; + +#if PAGE_SHIFT <= IA32_PAGE_SHIFT + ret = sys_mremap(addr, old_len, new_len, flags, new_addr); +#else + if (OFFSET4K(addr)) + return -EINVAL; + + old_len = IA32_PAGE_ALIGN(old_len); + new_len = IA32_PAGE_ALIGN(new_len); + old_end = addr + old_len; + new_end = addr + new_len; + + if (!new_len) + return -EINVAL; + + if ((flags & MREMAP_FIXED) && (OFFSET4K(new_addr))) + return -EINVAL; + + if (old_len >= new_len) { + ret = sys32_munmap(addr + new_len, old_len - new_len); + if (ret && old_len != new_len) + return ret; + ret = addr; + if (!(flags & MREMAP_FIXED) || (new_addr == addr)) + return ret; + old_len = new_len; + } + + addr = PAGE_START(addr); + old_len = PAGE_ALIGN(old_end) - addr; + new_len = PAGE_ALIGN(new_end) - addr; + + down(&ia32_mmap_sem); + { + ret = sys_mremap(addr, old_len, new_len, flags, new_addr); + } + up(&ia32_mmap_sem); + + if ((ret >= 0) && (old_len < new_len)) { /* mremap expand successfully */ + if (new_end <= PAGE_ALIGN(old_end)) { + /* old_end and new_end are in the same IA64 page */ + ia32_set_pp(old_end, new_end); + } else { + ia32_set_pp((unsigned int)PAGE_START(new_end), new_end); + ia32_set_pp(old_end, (unsigned int)PAGE_ALIGN(old_end)); + } + } +#endif + return ret; +} + asmlinkage long sys32_pipe (int *fd) { diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index 7455e489478c..29e07d0345c2 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -439,6 +439,10 @@ copy_thread (int nr, unsigned long clone_flags, ia32_save_state(p); if (clone_flags & CLONE_SETTLS) retval = ia32_clone_tls(p, child_ptregs); + + /* Copy partially mapped page list */ + if (!retval) + retval = ia32_copy_partial_page_list(p, clone_flags); } #endif @@ -672,6 +676,10 @@ flush_thread (void) /* drop floating-point and debug-register state if it exists: */ current->thread.flags &= ~(IA64_THREAD_FPH_VALID | IA64_THREAD_DBG_VALID); ia64_drop_fpu(current); +#ifdef CONFIG_IA32_SUPPORT + if (IS_IA32_PROCESS(ia64_task_regs(current))) + ia32_drop_partial_page_list(current->thread.ppl); +#endif } /* @@ -691,6 +699,10 @@ exit_thread (void) if (current->thread.flags & IA64_THREAD_DBG_VALID) pfm_release_debug_registers(current); #endif +#ifdef CONFIG_IA32_SUPPORT + if (IS_IA32_PROCESS(ia64_task_regs(current))) + ia32_drop_partial_page_list(current->thread.ppl); +#endif } unsigned long diff --git a/include/asm-ia64/ia32.h b/include/asm-ia64/ia32.h index 75dd0d334b99..a830b701c09d 100644 --- a/include/asm-ia64/ia32.h +++ b/include/asm-ia64/ia32.h @@ -18,6 +18,8 @@ extern void ia32_gdt_init (void); extern int ia32_exception (struct pt_regs *regs, unsigned long isr); extern int ia32_intercept (struct pt_regs *regs, unsigned long isr); extern int ia32_clone_tls (struct task_struct *child, struct pt_regs *childregs); +extern int ia32_copy_partial_page_list (struct task_struct *, unsigned long); +extern void ia32_drop_partial_page_list (struct partial_page_list *); # endif /* !CONFIG_IA32_SUPPORT */ diff --git a/include/asm-ia64/processor.h b/include/asm-ia64/processor.h index 05e9913e03b8..dec7d3fb1ba7 100644 --- a/include/asm-ia64/processor.h +++ b/include/asm-ia64/processor.h @@ -230,6 +230,7 @@ struct desc_struct { #define TLS_SIZE (GDT_ENTRY_TLS_ENTRIES * 8) +struct partial_page_list; #endif struct thread_struct { @@ -251,6 +252,7 @@ struct thread_struct { __u64 fdr; /* IA32 fp except. data reg */ __u64 old_k1; /* old value of ar.k1 */ __u64 old_iob; /* old IOBase value */ + struct partial_page_list *ppl; /* partial page list for 4K page size issue */ /* cached TLS descriptors. */ struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES]; @@ -260,7 +262,8 @@ struct thread_struct { .fir = 0, \ .fdr = 0, \ .old_k1 = 0, \ - .old_iob = 0, + .old_iob = 0, \ + .ppl = 0, #else # define INIT_THREAD_IA32 #endif /* CONFIG_IA32_SUPPORT */ -- cgit v1.2.3 From 549dc914078a2ca02233c761557bf9d2b0c3dc64 Mon Sep 17 00:00:00 2001 From: Keith Owens Date: Mon, 7 Jun 2004 07:38:30 -0700 Subject: [PATCH] ia64: Rename SN "modules" variable to "sn_modules". SN code has a "modules" variable that conflicts with a variable of the same name in kernel/module.c. This conflict breaks lcrash for ia64. Rename "modules" to "sn_modules". Signed-off-by: Keith Owens Signed-off-by: David Mosberger --- arch/ia64/sn/io/machvec/pci_bus_cvlink.c | 4 ++-- arch/ia64/sn/io/sn2/klgraph.c | 4 ++-- arch/ia64/sn/io/sn2/module.c | 14 +++++++------- include/asm-ia64/sn/module.h | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/arch/ia64/sn/io/machvec/pci_bus_cvlink.c b/arch/ia64/sn/io/machvec/pci_bus_cvlink.c index 1cb5beae5742..ba9e42c12ac5 100644 --- a/arch/ia64/sn/io/machvec/pci_bus_cvlink.c +++ b/arch/ia64/sn/io/machvec/pci_bus_cvlink.c @@ -741,7 +741,7 @@ pci_bus_to_hcl_cvlink(void) /* Is this PCI bus associated with this moduleid? */ moduleid = NODE_MODULEID( nasid_to_cnodeid(pcibr_soft->bs_nasid)); - if (modules[i]->id == moduleid) { + if (sn_modules[i]->id == moduleid) { struct pcibr_list_s *new_element; new_element = kmalloc(sizeof (struct pcibr_soft_s), GFP_KERNEL); @@ -781,7 +781,7 @@ pci_bus_to_hcl_cvlink(void) /* * We now have a list of all the pci bridges associated with - * the module_id, modules[i]. Call pci_bus_map_create() for + * the module_id, sn_modules[i]. Call pci_bus_map_create() for * each pci bridge */ softlistp = first_in_list; diff --git a/arch/ia64/sn/io/sn2/klgraph.c b/arch/ia64/sn/io/sn2/klgraph.c index ca342b7129b1..4fdffb877485 100644 --- a/arch/ia64/sn/io/sn2/klgraph.c +++ b/arch/ia64/sn/io/sn2/klgraph.c @@ -527,7 +527,7 @@ klhwg_add_all_modules(vertex_hdl_t hwgraph_root) /* Use module as module vertex fastinfo */ memset(buffer, 0, 16); - format_module_id(buffer, modules[cm]->id, MODULE_FORMAT_BRIEF); + format_module_id(buffer, sn_modules[cm]->id, MODULE_FORMAT_BRIEF); sprintf(name, EDGE_LBL_MODULE "/%s", buffer); rc = hwgraph_path_add(hwgraph_root, name, &module_vhdl); @@ -535,7 +535,7 @@ klhwg_add_all_modules(vertex_hdl_t hwgraph_root) rc = rc; HWGRAPH_DEBUG(__FILE__, __FUNCTION__, __LINE__, module_vhdl, NULL, "Created module path.\n"); - hwgraph_fastinfo_set(module_vhdl, (arbitrary_info_t) modules[cm]); + hwgraph_fastinfo_set(module_vhdl, (arbitrary_info_t) sn_modules[cm]); /* Add system controller */ sprintf(name, diff --git a/arch/ia64/sn/io/sn2/module.c b/arch/ia64/sn/io/sn2/module.c index 0bba7843e584..56e3188750c8 100644 --- a/arch/ia64/sn/io/sn2/module.c +++ b/arch/ia64/sn/io/sn2/module.c @@ -33,7 +33,7 @@ #define DPRINTF(x...) #endif -module_t *modules[MODULE_MAX]; +module_t *sn_modules[MODULE_MAX]; int nummodules; #define SN00_SERIAL_FUDGE 0x3b1af409d513c2 @@ -59,9 +59,9 @@ module_lookup(moduleid_t id) int i; for (i = 0; i < nummodules; i++) - if (modules[i]->id == id) { - DPRINTF("module_lookup: found m=0x%p\n", modules[i]); - return modules[i]; + if (sn_modules[i]->id == id) { + DPRINTF("module_lookup: found m=0x%p\n", sn_modules[i]); + return sn_modules[i]; } return NULL; @@ -104,10 +104,10 @@ module_add_node(geoid_t geoid, cnodeid_t cnodeid) /* Insert in sorted order by module number */ - for (i = nummodules; i > 0 && modules[i - 1]->id > moduleid; i--) - modules[i] = modules[i - 1]; + for (i = nummodules; i > 0 && sn_modules[i - 1]->id > moduleid; i--) + sn_modules[i] = sn_modules[i - 1]; - modules[i] = m; + sn_modules[i] = m; nummodules++; } diff --git a/include/asm-ia64/sn/module.h b/include/asm-ia64/sn/module.h index b7c7dd2aba2b..172f459fc01e 100644 --- a/include/asm-ia64/sn/module.h +++ b/include/asm-ia64/sn/module.h @@ -180,7 +180,7 @@ struct module_s { }; /* module.c */ -extern module_t *modules[MODULE_MAX]; /* Indexed by cmoduleid_t */ +extern module_t *sn_modules[MODULE_MAX]; /* Indexed by cmoduleid_t */ extern int nummodules; extern module_t *module_lookup(moduleid_t id); -- cgit v1.2.3 From 0ecfaf6853587914a3277c97fd8eb7ed4c9e7222 Mon Sep 17 00:00:00 2001 From: Keith Owens Date: Wed, 16 Jun 2004 02:04:19 -0700 Subject: [PATCH] ia64: Support SN platform specific error features The SN prom supports fine grained error handling features, the OS needs to tell the prom if the OS expects to use these platform specific features. Signed-off-by: Keith Owens Signed-off-by: David Mosberger --- arch/ia64/sn/kernel/setup.c | 23 ++++++++++++++++++++++- include/asm-ia64/sn/sn_sal.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/arch/ia64/sn/kernel/setup.c b/arch/ia64/sn/kernel/setup.c index 9644e5805cc4..da099657a9e8 100644 --- a/arch/ia64/sn/kernel/setup.c +++ b/arch/ia64/sn/kernel/setup.c @@ -226,7 +226,25 @@ sn_check_for_wars(void) shub_1_1_found = 1; } - +/** + * sn_set_error_handling_features - Tell the SN prom how to handle certain + * error types. + */ +static void __init +sn_set_error_handling_features(void) +{ + u64 ret; + u64 sn_ehf_bits[7]; /* see ia64_sn_set_error_handling_features */ + memset(sn_ehf_bits, 0, sizeof(sn_ehf_bits)); +#define EHF(x) __set_bit(SN_SAL_EHF_ ## x, sn_ehf_bits) + EHF(MCA_SLV_TO_OS_INIT_SLV); + EHF(NO_RZ_TLBC); + // Uncomment once Jesse's code goes in - EHF(NO_RZ_IO_READ); +#undef EHF + ret = ia64_sn_set_error_handling_features(sn_ehf_bits); + if (ret) + printk(KERN_ERR "%s: failed, return code %ld\n", __FUNCTION__, ret); +} /** * sn_setup - SN platform setup routine @@ -318,6 +336,9 @@ sn_setup(char **cmdline_p) master_node_bedrock_address); } + /* Tell the prom how to handle certain error types */ + sn_set_error_handling_features(); + /* * we set the default root device to /dev/hda * to make simulation easy diff --git a/include/asm-ia64/sn/sn_sal.h b/include/asm-ia64/sn/sn_sal.h index 0c2343152136..cc063429344f 100644 --- a/include/asm-ia64/sn/sn_sal.h +++ b/include/asm-ia64/sn/sn_sal.h @@ -33,6 +33,7 @@ #define SN_SAL_NO_FAULT_ZONE_VIRTUAL 0x02000010 #define SN_SAL_NO_FAULT_ZONE_PHYSICAL 0x02000011 #define SN_SAL_PRINT_ERROR 0x02000012 +#define SN_SAL_SET_ERROR_HANDLING_FEATURES 0x0200001a // reentrant #define SN_SAL_CONSOLE_PUTC 0x02000021 #define SN_SAL_CONSOLE_GETC 0x02000022 #define SN_SAL_CONSOLE_PUTS 0x02000023 @@ -92,6 +93,19 @@ #define SALRET_INVALID_ARG -2 #define SALRET_ERROR -3 +/* + * SN_SAL_SET_ERROR_HANDLING_FEATURES bit settings + */ +enum +{ + /* if "rz always" is set, have the mca slaves call os_init_slave */ + SN_SAL_EHF_MCA_SLV_TO_OS_INIT_SLV=0, + /* do not rz on tlb checks, even if "rz always" is set */ + SN_SAL_EHF_NO_RZ_TLBC, + /* do not rz on PIO reads to I/O space, even if "rz always" is set */ + SN_SAL_EHF_NO_RZ_IO_READ, +}; + /** * sn_sal_rev_major - get the major SGI SAL revision number @@ -670,4 +684,24 @@ ia64_sn_sysctl_iobrick_pci_op(nasid_t n, u64 connection_type, return 0; } +/* + * Tell the prom how the OS wants to handle specific error features. + * It takes an array of 7 u64. + */ +static inline u64 +ia64_sn_set_error_handling_features(const u64 *feature_bits) +{ + struct ia64_sal_retval rv = {0, 0, 0, 0}; + + SAL_CALL_REENTRANT(rv, SN_SAL_SET_ERROR_HANDLING_FEATURES, + feature_bits[0], + feature_bits[1], + feature_bits[2], + feature_bits[3], + feature_bits[4], + feature_bits[5], + feature_bits[6]); + return rv.status; +} + #endif /* _ASM_IA64_SN_SN_SAL_H */ -- cgit v1.2.3 From e3b1acd424b10aab727e259bae02fbe92c489570 Mon Sep 17 00:00:00 2001 From: David Mosberger Date: Wed, 16 Jun 2004 06:51:30 -0700 Subject: ia64: Fix ia32 partial-page-list code to compile cleanly in more configs. --- arch/ia64/ia32/ia32_support.c | 16 ++++++++++------ arch/ia64/ia32/ia32priv.h | 11 +++++++---- arch/ia64/ia32/sys_ia32.c | 13 ++++++++----- arch/ia64/kernel/entry.S | 6 ++++-- arch/ia64/kernel/head.S | 6 +++++- arch/ia64/kernel/process.c | 8 ++------ include/asm-ia64/ia32.h | 12 +++++++++--- 7 files changed, 45 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/arch/ia64/ia32/ia32_support.c b/arch/ia64/ia32/ia32_support.c index e1da7f8bd0c5..4b43f1753c0f 100644 --- a/arch/ia64/ia32/ia32_support.c +++ b/arch/ia64/ia32/ia32_support.c @@ -211,8 +211,6 @@ ia32_cpu_init (void) static int __init ia32_init (void) { - extern kmem_cache_t *partial_page_cachep; - ia32_exec_domain.name = "Linux/x86"; ia32_exec_domain.handler = NULL; ia32_exec_domain.pers_low = PER_LINUX32; @@ -221,11 +219,17 @@ ia32_init (void) ia32_exec_domain.signal_invmap = default_exec_domain.signal_invmap; register_exec_domain(&ia32_exec_domain); - partial_page_cachep = kmem_cache_create("partial_page_cache", - sizeof(struct partial_page), 0, 0, NULL, NULL); - if (!partial_page_cachep) - panic("Cannot create partial page SLAB cache"); +#if PAGE_SHIFT > IA32_PAGE_SHIFT + { + extern kmem_cache_t *partial_page_cachep; + partial_page_cachep = kmem_cache_create("partial_page_cache", + sizeof(struct partial_page), 0, 0, + NULL, NULL); + if (!partial_page_cachep) + panic("Cannot create partial page SLAB cache"); + } +#endif return 0; } diff --git a/arch/ia64/ia32/ia32priv.h b/arch/ia64/ia32/ia32priv.h index 8ffd9243248e..ba875e0bbbdc 100644 --- a/arch/ia64/ia32/ia32priv.h +++ b/arch/ia64/ia32/ia32priv.h @@ -1,5 +1,5 @@ -#ifndef _ASM_IA64_IA32_H -#define _ASM_IA64_IA32_H +#ifndef _ASM_IA64_IA32_PRIV_H +#define _ASM_IA64_IA32_PRIV_H #include @@ -17,7 +17,6 @@ * 32 bit structures for IA32 support. */ -#define IA32_PAGE_SHIFT 12 /* 4KB pages */ #define IA32_PAGE_SIZE (1UL << IA32_PAGE_SHIFT) #define IA32_PAGE_MASK (~(IA32_PAGE_SIZE - 1)) #define IA32_PAGE_ALIGN(addr) (((addr) + IA32_PAGE_SIZE - 1) & IA32_PAGE_MASK) @@ -45,7 +44,11 @@ struct partial_page_list { atomic_t pp_count; /* reference count */ }; +#if PAGE_SHIFT > IA32_PAGE_SHIFT struct partial_page_list* ia32_init_pp_list (void); +#else +# define ia32_init_pp_list() 0 +#endif /* sigcontext.h */ /* @@ -553,4 +556,4 @@ extern int save_ia32_fpxstate (struct task_struct *tsk, struct ia32_user_fxsr_st #endif /* !CONFIG_IA32_SUPPORT */ -#endif /* _ASM_IA64_IA32_H */ +#endif /* _ASM_IA64_IA32_PRIV_H */ diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index dcfb337ab72b..bc1b49d320aa 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -563,8 +563,10 @@ ia32_do_drop_pp_list(struct partial_page_list *ppl) } void -ia32_drop_partial_page_list(struct partial_page_list* ppl) +ia32_drop_partial_page_list(struct task_struct *task) { + struct partial_page_list* ppl = task->thread.ppl; + if (ppl && atomic_dec_and_test(&ppl->pp_count)) ia32_do_drop_pp_list(ppl); } @@ -855,14 +857,14 @@ sys32_mmap2 (unsigned int addr, unsigned int len, unsigned int prot, unsigned in asmlinkage long sys32_munmap (unsigned int start, unsigned int len) { - unsigned int end, pstart, pend; + unsigned int end = start + len; long ret; - end = start + len; - #if PAGE_SHIFT <= IA32_PAGE_SHIFT ret = sys_munmap(start, end - start); #else + unsigned int pstart, pend; + if (OFFSET4K(start)) return -EINVAL; @@ -1034,11 +1036,12 @@ sys32_mremap (unsigned int addr, unsigned int old_len, unsigned int new_len, unsigned int flags, unsigned int new_addr) { long ret; - unsigned int old_end, new_end; #if PAGE_SHIFT <= IA32_PAGE_SHIFT ret = sys_mremap(addr, old_len, new_len, flags, new_addr); #else + unsigned int old_end, new_end; + if (OFFSET4K(addr)) return -EINVAL; diff --git a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S index 45a49b600c20..d148a2c92174 100644 --- a/arch/ia64/kernel/entry.S +++ b/arch/ia64/kernel/entry.S @@ -179,17 +179,19 @@ GLOBAL_ENTRY(ia64_switch_to) .body adds r22=IA64_TASK_THREAD_KSP_OFFSET,r13 + movl r25=init_task mov r27=IA64_KR(CURRENT_STACK) + adds r21=IA64_TASK_THREAD_KSP_OFFSET,in0 dep r20=0,in0,61,3 // physical address of "current" ;; st8 [r22]=sp // save kernel stack pointer of old task shr.u r26=r20,IA64_GRANULE_SHIFT - adds r21=IA64_TASK_THREAD_KSP_OFFSET,in0 + cmp.eq p7,p6=r25,in0 ;; /* * If we've already mapped this task's page, we can skip doing it again. */ - cmp.eq p7,p6=r26,r27 +(p6) cmp.eq p7,p6=r26,r27 (p6) br.cond.dpnt .map ;; .done: diff --git a/arch/ia64/kernel/head.S b/arch/ia64/kernel/head.S index 0a5eb48d5de9..406d3e6ca797 100644 --- a/arch/ia64/kernel/head.S +++ b/arch/ia64/kernel/head.S @@ -154,6 +154,10 @@ start_ap: #endif ;; tpa r3=r2 // r3 == phys addr of task struct + ;; + shr.u r16=r3,IA64_GRANULE_SHIFT +(isBP) br.cond.dpnt .load_current // BP stack is on region 5 --- no need to map it + // load mapping for stack (virtaddr in r2, physaddr in r3) rsm psr.ic movl r17=PAGE_KERNEL @@ -165,7 +169,6 @@ start_ap: dep r2=-1,r3,61,3 // IMVA of task ;; mov r17=rr[r2] - shr.u r16=r3,IA64_GRANULE_SHIFT ;; dep r17=0,r17,8,24 ;; @@ -180,6 +183,7 @@ start_ap: srlz.d ;; +.load_current: // load the "current" pointer (r13) and ar.k6 with the current task mov IA64_KR(CURRENT)=r2 // virtual address mov IA64_KR(CURRENT_STACK)=r16 diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c index 29e07d0345c2..f49b98c6d3f3 100644 --- a/arch/ia64/kernel/process.c +++ b/arch/ia64/kernel/process.c @@ -676,10 +676,8 @@ flush_thread (void) /* drop floating-point and debug-register state if it exists: */ current->thread.flags &= ~(IA64_THREAD_FPH_VALID | IA64_THREAD_DBG_VALID); ia64_drop_fpu(current); -#ifdef CONFIG_IA32_SUPPORT if (IS_IA32_PROCESS(ia64_task_regs(current))) - ia32_drop_partial_page_list(current->thread.ppl); -#endif + ia32_drop_partial_page_list(current); } /* @@ -699,10 +697,8 @@ exit_thread (void) if (current->thread.flags & IA64_THREAD_DBG_VALID) pfm_release_debug_registers(current); #endif -#ifdef CONFIG_IA32_SUPPORT if (IS_IA32_PROCESS(ia64_task_regs(current))) - ia32_drop_partial_page_list(current->thread.ppl); -#endif + ia32_drop_partial_page_list(current); } unsigned long diff --git a/include/asm-ia64/ia32.h b/include/asm-ia64/ia32.h index a830b701c09d..4fa4b8e12000 100644 --- a/include/asm-ia64/ia32.h +++ b/include/asm-ia64/ia32.h @@ -6,7 +6,8 @@ #include #include -#define IA32_NR_syscalls 283 /* length of syscall table */ +#define IA32_NR_syscalls 283 /* length of syscall table */ +#define IA32_PAGE_SHIFT 12 /* 4KB pages */ #ifndef __ASSEMBLY__ @@ -18,14 +19,19 @@ extern void ia32_gdt_init (void); extern int ia32_exception (struct pt_regs *regs, unsigned long isr); extern int ia32_intercept (struct pt_regs *regs, unsigned long isr); extern int ia32_clone_tls (struct task_struct *child, struct pt_regs *childregs); -extern int ia32_copy_partial_page_list (struct task_struct *, unsigned long); -extern void ia32_drop_partial_page_list (struct partial_page_list *); # endif /* !CONFIG_IA32_SUPPORT */ /* Declare this unconditionally, so we don't get warnings for unreachable code. */ extern int ia32_setup_frame1 (int sig, struct k_sigaction *ka, siginfo_t *info, sigset_t *set, struct pt_regs *regs); +#if PAGE_SHIFT > IA32_PAGE_SHIFT +extern int ia32_copy_partial_page_list (struct task_struct *, unsigned long); +extern void ia32_drop_partial_page_list (struct task_struct *); +#else +# define ia32_copy_partial_page_list(a1, a2) 0 +# define ia32_drop_partial_page_list(a1) do { ; } while (0) +#endif #endif /* !__ASSEMBLY__ */ -- cgit v1.2.3 From b702883aff212202aa34b19a53c8602a72302bc3 Mon Sep 17 00:00:00 2001 From: David Mosberger Date: Thu, 17 Jun 2004 01:45:25 -0700 Subject: ia64: Squish some more hazards & warnings for UP compile. --- include/asm-ia64/gcc_intrin.h | 14 ++++++++++---- include/asm-ia64/system.h | 2 +- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/asm-ia64/gcc_intrin.h b/include/asm-ia64/gcc_intrin.h index d3d94e85c8eb..7d2b1e4cd0f9 100644 --- a/include/asm-ia64/gcc_intrin.h +++ b/include/asm-ia64/gcc_intrin.h @@ -489,10 +489,16 @@ register unsigned long ia64_r13 asm ("r13") __attribute_used__; #define ia64_ptce(addr) asm volatile ("ptc.e %0" :: "r"(addr)) #define ia64_ptcga(addr, size) \ - asm volatile ("ptc.ga %0,%1" :: "r"(addr), "r"(size) : "memory") +do { \ + asm volatile ("ptc.ga %0,%1" :: "r"(addr), "r"(size) : "memory"); \ + ia64_dv_serialize_data(); \ +} while (0) -#define ia64_ptcl(addr, size) \ - asm volatile ("ptc.l %0,%1" :: "r"(addr), "r"(size) : "memory") +#define ia64_ptcl(addr, size) \ +do { \ + asm volatile ("ptc.l %0,%1" :: "r"(addr), "r"(size) : "memory"); \ + ia64_dv_serialize_data(); \ +} while (0) #define ia64_ptri(addr, size) \ asm volatile ("ptr.i %0,%1" :: "r"(addr), "r"(size) : "memory") @@ -581,7 +587,7 @@ register unsigned long ia64_r13 asm ("r13") __attribute_used__; #define ia64_intrin_local_irq_restore(x) \ do { \ - asm volatile (" cmp.ne p6,p7=%0,r0;;" \ + asm volatile (";; cmp.ne p6,p7=%0,r0;;" \ "(p6) ssm psr.i;" \ "(p7) rsm psr.i;;" \ "(p6) srlz.d" \ diff --git a/include/asm-ia64/system.h b/include/asm-ia64/system.h index 57c7db10a42e..83b2c1bc0ffe 100644 --- a/include/asm-ia64/system.h +++ b/include/asm-ia64/system.h @@ -171,7 +171,7 @@ do { \ # define local_irq_restore(x) __local_irq_restore(x) #endif /* !CONFIG_IA64_DEBUG_IRQ */ -#define local_irq_enable() ({ ia64_ssm(IA64_PSR_I); ia64_srlz_d(); }) +#define local_irq_enable() ({ ia64_stop(); ia64_ssm(IA64_PSR_I); ia64_srlz_d(); }) #define local_save_flags(flags) ({ ia64_stop(); (flags) = ia64_getreg(_IA64_REG_PSR); }) #define irqs_disabled() \ -- cgit v1.2.3 From 72d6438c27c0730bf05a9cd1b1fcfe9040b3c790 Mon Sep 17 00:00:00 2001 From: David Mosberger Date: Thu, 17 Jun 2004 01:47:19 -0700 Subject: ia64: Fix build-problem when CONFIG_IA32 is not enabled. Without this fix, you'll get unresolved references to sys_rt_sigaction(). --- include/asm-ia64/unistd.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-ia64/unistd.h b/include/asm-ia64/unistd.h index ca3c7b958b1e..d46f47f9707e 100644 --- a/include/asm-ia64/unistd.h +++ b/include/asm-ia64/unistd.h @@ -266,6 +266,8 @@ #define NR_syscalls 256 /* length of syscall table */ +#define __ARCH_WANT_SYS_RT_SIGACTION + #ifdef CONFIG_IA32_SUPPORT # define __ARCH_WANT_SYS_FADVISE64 # define __ARCH_WANT_SYS_GETPGRP @@ -275,7 +277,6 @@ # define __ARCH_WANT_SYS_OLDUMOUNT # define __ARCH_WANT_SYS_SIGPENDING # define __ARCH_WANT_SYS_SIGPROCMASK -# define __ARCH_WANT_SYS_RT_SIGACTION #endif #if !defined(__ASSEMBLY__) && !defined(ASSEMBLER) -- cgit v1.2.3 From 8800eead12278010eb3f8987d85ff85ec6499e8a Mon Sep 17 00:00:00 2001 From: Ashok Raj Date: Fri, 18 Jun 2004 07:45:35 -0700 Subject: [PATCH] ia64: move move_irq() from iosapic.c to irq.c This patch moves move_irq() from ioaspic.c to irq.c to make this common for ia64 subarches. Signed-off-by: Ashok Raj Signed-off-by: David Mosberger --- arch/ia64/kernel/iosapic.c | 16 ---------------- arch/ia64/kernel/irq.c | 16 ++++++++++++++++ include/asm-ia64/irq.h | 6 ++++++ 3 files changed, 22 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c index 9ef5f3ef9dcb..4638a2dd845d 100644 --- a/arch/ia64/kernel/iosapic.c +++ b/arch/ia64/kernel/iosapic.c @@ -100,7 +100,6 @@ #endif static spinlock_t iosapic_lock = SPIN_LOCK_UNLOCKED; -extern cpumask_t __cacheline_aligned pending_irq_cpumask[NR_IRQS]; /* These tables map IA-64 vectors to the IOSAPIC pin that generates this vector. */ @@ -335,21 +334,6 @@ iosapic_set_affinity (unsigned int irq, cpumask_t mask) #endif } -static inline void move_irq(int irq) -{ - /* note - we hold desc->lock */ - cpumask_t tmp; - irq_desc_t *desc = irq_descp(irq); - - if (!cpus_empty(pending_irq_cpumask[irq])) { - cpus_and(tmp, pending_irq_cpumask[irq], cpu_online_map); - if (unlikely(!cpus_empty(tmp))) { - desc->handler->set_affinity(irq, pending_irq_cpumask[irq]); - } - cpus_clear(pending_irq_cpumask[irq]); - } -} - /* * Handlers for level-triggered interrupts. */ diff --git a/arch/ia64/kernel/irq.c b/arch/ia64/kernel/irq.c index 42056fbaa151..3087c13d08d7 100644 --- a/arch/ia64/kernel/irq.c +++ b/arch/ia64/kernel/irq.c @@ -1018,6 +1018,22 @@ static int irq_affinity_write_proc (struct file *file, const char *buffer, return full_count; } +void move_irq(int irq) +{ + /* note - we hold desc->lock */ + cpumask_t tmp; + irq_desc_t *desc = irq_descp(irq); + + if (!cpus_empty(pending_irq_cpumask[irq])) { + cpus_and(tmp, pending_irq_cpumask[irq], cpu_online_map); + if (unlikely(!cpus_empty(tmp))) { + desc->handler->set_affinity(irq, pending_irq_cpumask[irq]); + } + cpus_clear(pending_irq_cpumask[irq]); + } +} + + #endif /* CONFIG_SMP */ #ifdef CONFIG_HOTPLUG_CPU diff --git a/include/asm-ia64/irq.h b/include/asm-ia64/irq.h index 5d930fdc0bea..bd07d11d9f37 100644 --- a/include/asm-ia64/irq.h +++ b/include/asm-ia64/irq.h @@ -30,6 +30,12 @@ extern void disable_irq_nosync (unsigned int); extern void enable_irq (unsigned int); extern void set_irq_affinity_info (unsigned int irq, int dest, int redir); +#ifdef CONFIG_SMP +extern void move_irq(int irq); +#else +#define move_irq(irq) +#endif + struct irqaction; struct pt_regs; int handle_IRQ_event(unsigned int, struct pt_regs *, struct irqaction *); -- cgit v1.2.3 From 093199df3ed5089625c481e190e0a12273c0ef30 Mon Sep 17 00:00:00 2001 From: Ian Campbell Date: Fri, 18 Jun 2004 19:49:27 +0100 Subject: [ARM PATCH] 1934/2: Consolidate code to set CKEN on PXA Patch from Ian Campbell I've seen comments several times that various PXA drivers update CKEN in an unsafe manner. This patch consolidates this code into a single function pxa_set_cken() and updates all the in tree drivers to use it. --- arch/arm/mach-pxa/generic.c | 18 ++++++++++++++++++ drivers/serial/pxa.c | 9 ++------- drivers/usb/gadget/pxa2xx_udc.c | 4 ++-- drivers/video/pxafb.c | 5 +---- include/asm-arm/arch-pxa/hardware.h | 5 +++++ 5 files changed, 28 insertions(+), 13 deletions(-) (limited to 'include') diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c index 24f3da29df3e..20b776ff92d2 100644 --- a/arch/arm/mach-pxa/generic.c +++ b/arch/arm/mach-pxa/generic.c @@ -58,6 +58,24 @@ void pxa_gpio_mode(int gpio_mode) EXPORT_SYMBOL(pxa_gpio_mode); +/* + * Routine to safely enable or disable a clock in the CKEN + */ +void pxa_set_cken(int clock, int enable) +{ + unsigned long flags; + local_irq_save(flags); + + if (enable) + CKEN |= clock; + else + CKEN &= ~clock; + + local_irq_restore(flags); +} + +EXPORT_SYMBOL(pxa_set_cken); + /* * Intel PXA2xx internal register mapping. * diff --git a/drivers/serial/pxa.c b/drivers/serial/pxa.c index 082e1b552cad..e83bb0728a37 100644 --- a/drivers/serial/pxa.c +++ b/drivers/serial/pxa.c @@ -571,14 +571,9 @@ serial_pxa_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { struct uart_pxa_port *up = (struct uart_pxa_port *)port; - if (state) { - /* sleep */ - CKEN &= ~up->cken; - } else { - /* wake */ - CKEN |= up->cken; + pxa_set_cken(up->cken, !state); + if (!state) udelay(1); - } } static void serial_pxa_release_port(struct uart_port *port) diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index 4ad4d2f7fd95..a74d64c00a9c 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -1406,7 +1406,7 @@ static void udc_disable(struct pxa2xx_udc *dev) #ifdef CONFIG_ARCH_PXA /* Disable clock for USB device */ - CKEN &= ~CKEN11_USB; + pxa_set_cken(CKEN11_USB, 0); #endif ep0_idle (dev); @@ -1452,7 +1452,7 @@ static void udc_enable (struct pxa2xx_udc *dev) #ifdef CONFIG_ARCH_PXA /* Enable clock for USB device */ - CKEN |= CKEN11_USB; + pxa_set_cken(CKEN11_USB, 1); #endif /* try to clear these bits before we enable the udc */ diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index e7ead056ffb2..05d8ad2a61c1 100644 --- a/drivers/video/pxafb.c +++ b/drivers/video/pxafb.c @@ -1237,7 +1237,6 @@ int __init pxafb_probe(struct device *dev) { struct pxafb_info *fbi; struct pxafb_mach_info *inf; - unsigned long flags; int ret; dev_dbg(dev, "pxafb_probe\n"); @@ -1301,9 +1300,7 @@ int __init pxafb_probe(struct device *dev) goto failed; } /* enable LCD controller clock */ - local_irq_save(flags); - CKEN |= CKEN16_LCD; - local_irq_restore(flags); + pxa_set_cken(CKEN16_LCD, 1); ret = request_irq(IRQ_LCD, pxafb_handle_irq, SA_INTERRUPT, "LCD", fbi); if (ret) { diff --git a/include/asm-arm/arch-pxa/hardware.h b/include/asm-arm/arch-pxa/hardware.h index ffd2fa2eb502..127fc1af12c0 100644 --- a/include/asm-arm/arch-pxa/hardware.h +++ b/include/asm-arm/arch-pxa/hardware.h @@ -82,6 +82,11 @@ typedef struct { volatile u32 offset[4096]; } __regbase; */ extern void pxa_gpio_mode( int gpio_mode ); +/* + * Routine to enable or disable CKEN + */ +extern void pxa_set_cken(int clock, int enable); + /* * return current memory and LCD clock frequency in units of 10kHz */ -- cgit v1.2.3 From 5397fc64efc184c135f1a803c0cbf7de38f500e8 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 18 Jun 2004 19:58:33 +0100 Subject: [ARM PATCH] 1931/1: Allow device address translation in dma-mapping, version 3 Patch from Tony Lindgren Allows translation of shared memory addresses for devices using dma-mapping. In some cases the device DMA address is different from the ARM DMA address, for example with some USB OHCI controllers. For more background information, please see the ARM Linux mailing list thread "OHCI controller". --- arch/arm/common/dmabounce.c | 10 +++++----- arch/arm/mm/consistent.c | 2 +- include/asm-arm/arch-omap/memory.h | 24 ++++++++++++++++++++---- include/asm-arm/dma-mapping.h | 8 ++++---- include/asm-arm/memory.h | 11 ++++++++++- 5 files changed, 40 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c index 0d0d8e8821c6..c3a87663b040 100644 --- a/arch/arm/common/dmabounce.c +++ b/arch/arm/common/dmabounce.c @@ -234,7 +234,7 @@ map_single(struct device *dev, void *ptr, size_t size, } } - dma_addr = virt_to_bus(ptr); + dma_addr = virt_to_dma(dev, ptr); if (device_info && dma_needs_bounce(dev, dma_addr, size)) { struct safe_buffer *buf; @@ -248,7 +248,7 @@ map_single(struct device *dev, void *ptr, size_t size, dev_dbg(dev, "%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n", - __func__, buf->ptr, (void *) virt_to_bus(buf->ptr), + __func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr), buf->safe, (void *) buf->safe_dma_addr); if ((dir == DMA_TO_DEVICE) || @@ -290,7 +290,7 @@ unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size, dev_dbg(dev, "%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n", - __func__, buf->ptr, (void *) virt_to_bus(buf->ptr), + __func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr), buf->safe, (void *) buf->safe_dma_addr); @@ -342,7 +342,7 @@ sync_single(struct device *dev, dma_addr_t dma_addr, size_t size, dev_dbg(dev, "%s: unsafe buffer %p (phy=%p) mapped to %p (phy=%p)\n", - __func__, buf->ptr, (void *) virt_to_bus(buf->ptr), + __func__, buf->ptr, (void *) virt_to_dma(dev, buf->ptr), buf->safe, (void *) buf->safe_dma_addr); DO_STATS ( device_info->bounce_count++ ); @@ -367,7 +367,7 @@ sync_single(struct device *dev, dma_addr_t dma_addr, size_t size, } consistent_sync(buf->safe, size, dir); } else { - consistent_sync(bus_to_virt(dma_addr), size, dir); + consistent_sync(dma_to_virt(dev, dma_addr), size, dir); } } diff --git a/arch/arm/mm/consistent.c b/arch/arm/mm/consistent.c index 7d3fb20ea212..f9b8fe2c7594 100644 --- a/arch/arm/mm/consistent.c +++ b/arch/arm/mm/consistent.c @@ -194,7 +194,7 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, int gfp, /* * Set the "dma handle" */ - *handle = page_to_bus(page); + *handle = page_to_dma(dev, page); do { BUG_ON(!pte_none(*pte)); diff --git a/include/asm-arm/arch-omap/memory.h b/include/asm-arm/arch-omap/memory.h index 106af7555813..4f9e8c842d6d 100644 --- a/include/asm-arm/arch-omap/memory.h +++ b/include/asm-arm/arch-omap/memory.h @@ -57,12 +57,28 @@ /* * OMAP-1510 bus address is translated into a Local Bus address if the - * OMAP bus type is lbus. See dmadev_uses_omap_lbus(). + * OMAP bus type is lbus. We do the address translation based on the + * device overriding the defaults used in the dma-mapping API. */ #ifdef CONFIG_ARCH_OMAP1510 -#define bus_to_lbus(x) ((x) + (OMAP1510_LB_OFFSET - PHYS_OFFSET)) -#define lbus_to_bus(x) ((x) - (OMAP1510_LB_OFFSET - PHYS_OFFSET)) -#endif + +#define virt_to_lbus(x) ((x) - PAGE_OFFSET + OMAP1510_LB_OFFSET) +#define lbus_to_virt(x) ((x) - OMAP1510_LB_OFFSET + PAGE_OFFSET) +#define is_lbus_device(dev) (cpu_is_omap1510() && dev->coherent_dma_mask == 0x0fffffff) + +#define __arch_page_to_dma(dev, page) ({is_lbus_device(dev) ? \ + (dma_addr_t)virt_to_lbus(page_address(page)) : \ + (dma_addr_t)__virt_to_bus(page_address(page));}) + +#define __arch_dma_to_virt(dev, addr) ({is_lbus_device(dev) ? \ + lbus_to_virt(addr) : \ + __bus_to_virt(addr);}) + +#define __arch_virt_to_dma(dev, addr) ({is_lbus_device(dev) ? \ + virt_to_lbus(addr) : \ + __virt_to_bus(addr);}) + +#endif /* CONFIG_ARCH_OMAP1510 */ #define PHYS_TO_NID(addr) (0) #endif diff --git a/include/asm-arm/dma-mapping.h b/include/asm-arm/dma-mapping.h index 011c539c7449..b8750631dd09 100644 --- a/include/asm-arm/dma-mapping.h +++ b/include/asm-arm/dma-mapping.h @@ -124,7 +124,7 @@ dma_map_single(struct device *dev, void *cpu_addr, size_t size, enum dma_data_direction dir) { consistent_sync(cpu_addr, size, dir); - return __virt_to_bus((unsigned long)cpu_addr); + return virt_to_dma(dev, (unsigned long)cpu_addr); } #else extern dma_addr_t dma_map_single(struct device *,void *, size_t, enum dma_data_direction); @@ -231,7 +231,7 @@ dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, for (i = 0; i < nents; i++, sg++) { char *virt; - sg->dma_address = page_to_bus(sg->page) + sg->offset; + sg->dma_address = page_to_dma(dev, sg->page) + sg->offset; virt = page_address(sg->page) + sg->offset; consistent_sync(virt, sg->length, dir); } @@ -288,14 +288,14 @@ static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { - consistent_sync((void *)__bus_to_virt(handle), size, dir); + consistent_sync((void *)dma_to_virt(dev, handle), size, dir); } static inline void dma_sync_single_for_device(struct device *dev, dma_addr_t handle, size_t size, enum dma_data_direction dir) { - consistent_sync((void *)__bus_to_virt(handle), size, dir); + consistent_sync((void *)dma_to_virt(dev, handle), size, dir); } #else extern void dma_sync_single_for_cpu(struct device*, dma_addr_t, size_t, enum dma_data_direction); diff --git a/include/asm-arm/memory.h b/include/asm-arm/memory.h index 75da0b9c4960..91dee4da45ce 100644 --- a/include/asm-arm/memory.h +++ b/include/asm-arm/memory.h @@ -159,9 +159,18 @@ static inline void *phys_to_virt(unsigned long x) #define page_to_phys(page) (page_to_pfn(page) << PAGE_SHIFT) /* + * Optional device DMA address remapping. Do _not_ use directly! * We should really eliminate virt_to_bus() here - it's deprecated. */ -#define page_to_bus(page) (virt_to_bus(page_address(page))) +#ifndef __arch_page_to_dma +#define page_to_dma(dev, page) ((dma_addr_t)__virt_to_bus(page_address(page))) +#define dma_to_virt(dev, addr) (__bus_to_virt(addr)) +#define virt_to_dma(dev, addr) (__virt_to_bus(addr)) +#else +#define page_to_dma(dev, page) (__arch_page_to_dma(dev, page)) +#define dma_to_virt(dev, addr) (__arch_dma_to_virt(dev, addr)) +#define virt_to_dma(dev, addr) (__arch_virt_to_dma(dev, addr)) +#endif #endif -- cgit v1.2.3 From c93f83132f55e4f164a533f1023d230dfd64ea13 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 18 Jun 2004 21:45:27 +0100 Subject: [ARM] Remove TBOX. This platform is no longer maintained, and its maintainer says that the hardware is obsolete and out of circulation. --- arch/arm/Makefile | 1 - arch/arm/mach-tbox/Makefile | 11 ----- arch/arm/mach-tbox/core.c | 73 ---------------------------------- include/asm-arm/arch-tbox/dma.h | 37 ----------------- include/asm-arm/arch-tbox/hardware.h | 60 ---------------------------- include/asm-arm/arch-tbox/io.h | 40 ------------------- include/asm-arm/arch-tbox/irqs.h | 29 -------------- include/asm-arm/arch-tbox/memory.h | 21 ---------- include/asm-arm/arch-tbox/param.h | 4 -- include/asm-arm/arch-tbox/serial.h | 32 --------------- include/asm-arm/arch-tbox/system.h | 16 -------- include/asm-arm/arch-tbox/time.h | 39 ------------------ include/asm-arm/arch-tbox/timex.h | 8 ---- include/asm-arm/arch-tbox/uncompress.h | 42 ------------------- include/asm-arm/arch-tbox/vmalloc.h | 15 ------- 15 files changed, 428 deletions(-) delete mode 100644 arch/arm/mach-tbox/Makefile delete mode 100644 arch/arm/mach-tbox/core.c delete mode 100644 include/asm-arm/arch-tbox/dma.h delete mode 100644 include/asm-arm/arch-tbox/hardware.h delete mode 100644 include/asm-arm/arch-tbox/io.h delete mode 100644 include/asm-arm/arch-tbox/irqs.h delete mode 100644 include/asm-arm/arch-tbox/memory.h delete mode 100644 include/asm-arm/arch-tbox/param.h delete mode 100644 include/asm-arm/arch-tbox/serial.h delete mode 100644 include/asm-arm/arch-tbox/system.h delete mode 100644 include/asm-arm/arch-tbox/time.h delete mode 100644 include/asm-arm/arch-tbox/timex.h delete mode 100644 include/asm-arm/arch-tbox/uncompress.h delete mode 100644 include/asm-arm/arch-tbox/vmalloc.h (limited to 'include') diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 598a521ce18e..2f79fb2a3ed6 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -76,7 +76,6 @@ textaddr-$(CONFIG_ARCH_CO285) := 0x60008000 incdir-$(CONFIG_ARCH_CO285) := ebsa285 machine-$(CONFIG_ARCH_FTVPCI) := ftvpci incdir-$(CONFIG_ARCH_FTVPCI) := nexuspci - machine-$(CONFIG_ARCH_TBOX) := tbox machine-$(CONFIG_ARCH_SHARK) := shark machine-$(CONFIG_ARCH_SA1100) := sa1100 ifeq ($(CONFIG_ARCH_SA1100),y) diff --git a/arch/arm/mach-tbox/Makefile b/arch/arm/mach-tbox/Makefile deleted file mode 100644 index 4bd8ebd70e7b..000000000000 --- a/arch/arm/mach-tbox/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# -# Makefile for the linux kernel. -# - -# Object file lists. - -obj-y := core.o -obj-m := -obj-n := -obj- := - diff --git a/arch/arm/mach-tbox/core.c b/arch/arm/mach-tbox/core.c deleted file mode 100644 index db9ac783cfce..000000000000 --- a/arch/arm/mach-tbox/core.c +++ /dev/null @@ -1,73 +0,0 @@ -/* - * linux/arch/arm/mm/mm-tbox.c - * - * Copyright (C) 1998, 1999, 2000 Phil Blundell - * Copyright (C) 1998-1999 Russell King - * - * Extra MM routines for the Tbox architecture - */ -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include - -extern unsigned long soft_irq_mask; - -static void tbox_mask_irq(unsigned int irq) -{ - __raw_writel(0, INTCONT + (irq << 2)); - soft_irq_mask &= ~(1<= 12 && i <= 13)) { - irq_desc[i].valid = 1; - irq_desc[i].probe_ok = 0; - irq_desc[i].mask_ack = tbox_mask_irq; - irq_desc[i].mask = tbox_mask_irq; - irq_desc[i].unmask = tbox_unmask_irq; - tbox_mask_irq(i); - } else { - irq_desc[i].valid = 0; - irq_desc[i].probe_ok = 0; - } - } -} - -static struct map_desc tbox_io_desc[] __initdata = { - /* See hardware.h for details */ - { IO_BASE, IO_START, 0x00100000, MT_DEVICE } -}; - -static void __init tbox_map_io(void) -{ - iotable_init(tbox_io_desc, ARRAY_SIZE(tbox_io_desc)); -} - -MACHINE_START(TBOX, "unknown-TBOX") - MAINTAINER("Philip Blundell") - BOOT_MEM(0x80000000, 0x00400000, 0xe0000000) - MAPIO(tbox_map_io) - INITIRQ(tbox_init_irq) -MACHINE_END - diff --git a/include/asm-arm/arch-tbox/dma.h b/include/asm-arm/arch-tbox/dma.h deleted file mode 100644 index 1d5d39175671..000000000000 --- a/include/asm-arm/arch-tbox/dma.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * linux/include/asm-arm/arch-tbox/dma.h - * - * Architecture DMA routines. We have to contend with the bizarre DMA - * machine built into the Tbox hardware. - * - * Copyright (C) 1998 Philip Blundell - */ - -/* - * 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. - */ - -/* - * DMA channel definitions. Some of these are physically strange but - * we sort it out inside dma.c so the user never has to care. The - * exception is the double-buffering which we can't really abstract - * away sensibly. - */ -#define DMA_VIDEO 0 -#define DMA_MPEG_B 1 -#define DMA_AUDIO_B 2 -#define DMA_ASHRX_B 3 -#define DMA_ASHTX 4 -#define DMA_MPEG 5 -#define DMA_AUDIO 6 -#define DMA_ASHRX 7 - -#define MAX_DMA_CHANNELS 0 /* XXX */ - -/* - * This is the maximum DMA address that can be DMAd to. - */ -#define MAX_DMA_ADDRESS 0xffffffff diff --git a/include/asm-arm/arch-tbox/hardware.h b/include/asm-arm/arch-tbox/hardware.h deleted file mode 100644 index 9aa3f4508b46..000000000000 --- a/include/asm-arm/arch-tbox/hardware.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * linux/include/asm-arm/arch-tbox/hardware.h - * - * Copyright (C) 1998, 1999, 2000 Philip Blundell - * Copyright (C) 2000 FutureTV Labs Ltd - * - * This file contains the hardware definitions of the Tbox - */ - -/* - * 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 __ASM_ARCH_HARDWARE_H -#define __ASM_ARCH_HARDWARE_H - -/* Logical Physical - * 0xfff00000 0x00100000 I/O - * 0xfff00000 0x00100000 Expansion CS0 - * 0xfff10000 0x00110000 DMA - * 0xfff20000 0x00120000 C-Cube - * 0xfff30000 0x00130000 FPGA 1 - * 0xfff40000 0x00140000 UART 2 - * 0xfff50000 0x00150000 UART 1 - * 0xfff60000 0x00160000 CS8900 - * 0xfff70000 0x00170000 INTCONT - * 0xfff80000 0x00180000 RAMDAC - * 0xfff90000 0x00190000 Control 0 - * 0xfffa0000 0x001a0000 Control 1 - * 0xfffb0000 0x001b0000 Control 2 - * 0xfffc0000 0x001c0000 FPGA 2 - * 0xfffd0000 0x001d0000 INTRESET - * 0xfffe0000 0x001e0000 C-Cube DMA throttle - * 0xffff0000 0x001f0000 Expansion CS1 - * 0xffe00000 0x82000000 cache flush - */ - -/* - * Mapping areas - */ -#define IO_BASE 0xfff00000 -#define IO_START 0x00100000 -#define FLUSH_BASE 0xffe00000 - -#define INTCONT 0xfff70000 - -#define FPGA1CONT 0xffff3000 - -/* - * RAM definitions - */ -#define RAM_BASE 0x80000000 -#define FLUSH_BASE_PHYS 0x82000000 - -#define UNCACHEABLE_ADDR INTCONT - -#endif diff --git a/include/asm-arm/arch-tbox/io.h b/include/asm-arm/arch-tbox/io.h deleted file mode 100644 index 869798595c94..000000000000 --- a/include/asm-arm/arch-tbox/io.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * linux/include/asm-arm/arch-tbox/io.h - * - * Copyright (C) 1996-1999 Russell King - * Copyright (C) 1998, 1999 Philip Blundell - * - */ -#ifndef __ASM_ARM_ARCH_IO_H -#define __ASM_ARM_ARCH_IO_H - -#define IO_SPACE_LIMIT 0xffffffff - -#define __io(_x) ((_x) << 2) - -/* - * Generic virtual read/write - */ -static inline unsigned int __arch_getw(unsigned long a) -{ - unsigned int value; - __asm__ __volatile__("ldr%?h %0, [%1, #0] @ getw" - : "=&r" (value) - : "r" (a)); - return value; -} - -static inline void __arch_putw(unsigned int value, unsigned long a) -{ - __asm__ __volatile__("str%?h %0, [%1, #0] @ putw" - : : "r" (value), "r" (a)); -} - -/* Idem, for devices on the upper byte lanes */ -#define inb_u(p) __arch_getb(__io_pc(p) + 2) -#define inw_u(p) __arch_getw(__io_pc(p) + 2) - -#define outb_u(v,p) __arch_putb(v,__io_pc(p) + 2) -#define outw_u(v,p) __arch_putw(v,__io_pc(p) + 2) - -#endif diff --git a/include/asm-arm/arch-tbox/irqs.h b/include/asm-arm/arch-tbox/irqs.h deleted file mode 100644 index 1ee5eba6ea9d..000000000000 --- a/include/asm-arm/arch-tbox/irqs.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * linux/include/asm-arm/arch-tbox/irqs.h - * - * Copyright (C) 1998, 2000 Philip Blundell - */ - -/* - * 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. - */ - -#define IRQ_MPEGDMA 0 -#define IRQ_ASHTX 1 -#define IRQ_ASHRX 2 -#define IRQ_VSYNC 3 -#define IRQ_HSYNC 4 -#define IRQ_MPEG 5 -#define IRQ_UART2 6 -#define IRQ_UART1 7 -#define IRQ_ETHERNET 8 -#define IRQ_TIMER 9 -#define IRQ_AUDIODMA 10 -/* bit 11 used for video field ident */ -#define IRQ_EXPMODCS0 12 -#define IRQ_EXPMODCS1 13 - -#define irq_canonicalize(i) (i) diff --git a/include/asm-arm/arch-tbox/memory.h b/include/asm-arm/arch-tbox/memory.h deleted file mode 100644 index a20479487c9d..000000000000 --- a/include/asm-arm/arch-tbox/memory.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * linux/include/asm-arm/arch-tbox/memory.h - * - * Copyright (c) 1996-1999 Russell King. - * Copyright (c) 1998-1999 Phil Blundell - */ -#ifndef __ASM_ARCH_MEMORY_H -#define __ASM_ARCH_MEMORY_H - -/* - * Physical DRAM offset. - */ -#define PHYS_OFFSET (0x80000000UL) - -/* - * Bus view is the same as physical view - */ -#define __virt_to_bus(x) __virt_to_phys(x) -#define __bus_to_virt(x) __phys_to_virt(x) - -#endif diff --git a/include/asm-arm/arch-tbox/param.h b/include/asm-arm/arch-tbox/param.h deleted file mode 100644 index 4b47fe32b7c6..000000000000 --- a/include/asm-arm/arch-tbox/param.h +++ /dev/null @@ -1,4 +0,0 @@ -/* - * linux/include/asm-arm/arch-tbox/param.h - */ -#define __KERNEL_HZ 1000 diff --git a/include/asm-arm/arch-tbox/serial.h b/include/asm-arm/arch-tbox/serial.h deleted file mode 100644 index 7e4aff996fd8..000000000000 --- a/include/asm-arm/arch-tbox/serial.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * linux/include/asm-arm/arch-tbox/serial.h - * - * Copyright (c) 1996 Russell King. - * Copyright (c) 1998 Phil Blundell - * - * Changelog: - * 15-10-1996 RMK Created - * 09-06-1998 PJB tbox version - */ -#ifndef __ASM_ARCH_SERIAL_H -#define __ASM_ARCH_SERIAL_H - -/* - * This assumes you have a 1.8432 MHz clock for your UART. - * - * It'd be nice if someone built a serial card with a 24.576 MHz - * clock, since the 16550A is capable of handling a top speed of 1.5 - * megabits/second; but this requires the faster clock. - */ -#define BASE_BAUD (1843200 / 16) - -#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST) - - /* UART CLK PORT IRQ FLAGS */ -#define STD_SERIAL_PORT_DEFNS \ - { 0, BASE_BAUD, 0xffff4000 >> 2, 6, STD_COM_FLAGS }, /* ttyS0 */ \ - { 0, BASE_BAUD, 0xffff5000 >> 2, 7, STD_COM_FLAGS }, /* ttyS1 */ - -#define EXTRA_SERIAL_PORT_DEFNS - -#endif diff --git a/include/asm-arm/arch-tbox/system.h b/include/asm-arm/arch-tbox/system.h deleted file mode 100644 index da2cb88acb0c..000000000000 --- a/include/asm-arm/arch-tbox/system.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * linux/include/asm-arm/arch-tbox/system.h - * - * Copyright (c) 1996-1999 Russell King. - */ -#ifndef __ASM_ARCH_SYSTEM_H -#define __ASM_ARCH_SYSTEM_H - -static inline void arch_idle(void) -{ - cpu_do_idle(); -} - -#define arch_reset(mode) do { } while (0) - -#endif diff --git a/include/asm-arm/arch-tbox/time.h b/include/asm-arm/arch-tbox/time.h deleted file mode 100644 index 461787189e1e..000000000000 --- a/include/asm-arm/arch-tbox/time.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * linux/include/asm-arm/arch-tbox/time.h - * - * Copyright (c) 1997, 1999 Phil Blundell. - * Copyright (c) 2000 FutureTV Labs Ltd - * - * Tbox has no real-time clock -- we get millisecond ticks to update - * our soft copy. - */ - -/* - * 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 - -#define update_rtc() - -static irqreturn_t -timer_interrupt (int irq, void *dev_id, struct pt_regs *regs) -{ - /* Clear irq */ - __raw_writel(1, FPGA1CONT + 0xc); - __raw_writel(0, FPGA1CONT + 0xc); - - do_timer(regs); - - return IRQ_HANDLED; -} - -void __init time_init(void) -{ - timer_irq.handler = timer_interrupt; - setup_irq(IRQ_TIMER, &timer_irq); -} diff --git a/include/asm-arm/arch-tbox/timex.h b/include/asm-arm/arch-tbox/timex.h deleted file mode 100644 index c5489cd66ce7..000000000000 --- a/include/asm-arm/arch-tbox/timex.h +++ /dev/null @@ -1,8 +0,0 @@ -/* - * linux/include/asm-arm/arch-tbox/timex.h - * - * Tbox timex specifications - * - * Copyright (C) 1999 Philip Blundell - */ - diff --git a/include/asm-arm/arch-tbox/uncompress.h b/include/asm-arm/arch-tbox/uncompress.h deleted file mode 100644 index 17a5034e7812..000000000000 --- a/include/asm-arm/arch-tbox/uncompress.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/uncompress.h - * from linux/include/asm-arm/arch-ebsa110/uncompress.h - * - * Copyright (C) 1996,1997,1998 Russell King - * Copyright (C) 1998, 1999 Phil Blundell - */ - -#include - -#define UARTBASE 0x00400000 - -/* - * This does not append a newline - */ -static void puts(const char *s) -{ - while (*s) - { - char c = *(s++); - while (!(__raw_readb(UARTBASE + 0x14) & 0x20)); - __raw_writeb(c, UARTBASE); - if (c == 10) { - while (!(__raw_readb(UARTBASE + 0x14) & 0x20)); - __raw_writeb(13, UARTBASE); - } - } -} - -/* - * nothing to do - */ -#define arch_decomp_setup() - -/* - * Stroke the watchdog so we don't get reset during decompression. - */ -#define arch_decomp_wdog() \ - do { \ - __raw_writel(1, 0xa00000); \ - __raw_writel(0, 0xa00000); \ - } while (0) diff --git a/include/asm-arm/arch-tbox/vmalloc.h b/include/asm-arm/arch-tbox/vmalloc.h deleted file mode 100644 index da4a5c04f193..000000000000 --- a/include/asm-arm/arch-tbox/vmalloc.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * linux/include/asm-arm/arch-tbox/vmalloc.h - */ - -/* - * Just any arbitrary offset to the start of the vmalloc VM area: the - * current 8MB value just means that there will be a 8MB "hole" after the - * physical memory until the kernel virtual memory starts. That means that - * any out-of-bounds memory accesses will hopefully be caught. - * The vmalloc() routines leaves a hole of 4kB between each vmalloced - * area for the same reason. ;) - */ -#define VMALLOC_OFFSET (8*1024*1024) -#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) -#define VMALLOC_END (PAGE_OFFSET + 0x10000000) -- cgit v1.2.3 From e9479a8d1ee904b0780f65d889f5c5ed43172e5c Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 18 Jun 2004 21:53:46 +0100 Subject: [ARM] Remove NexusPCI/FTVPCI platform. This platform is no longer maintained, and its maintainer says that the hardware is obsolete and out of circulation. --- arch/arm/Kconfig | 13 +-- arch/arm/Makefile | 2 - arch/arm/common/Makefile | 1 - arch/arm/common/plx90x0.c | 178 ----------------------------- arch/arm/mach-ftvpci/Makefile | 13 --- arch/arm/mach-ftvpci/core.c | 96 ---------------- arch/arm/mach-ftvpci/leds.c | 36 ------ arch/arm/mach-ftvpci/pci.c | 60 ---------- include/asm-arm/arch-nexuspci/dma.h | 20 ---- include/asm-arm/arch-nexuspci/hardware.h | 76 ------------ include/asm-arm/arch-nexuspci/io.h | 58 ---------- include/asm-arm/arch-nexuspci/irqs.h | 34 ------ include/asm-arm/arch-nexuspci/memory.h | 23 ---- include/asm-arm/arch-nexuspci/param.h | 3 - include/asm-arm/arch-nexuspci/system.h | 24 ---- include/asm-arm/arch-nexuspci/time.h | 62 ---------- include/asm-arm/arch-nexuspci/timex.h | 8 -- include/asm-arm/arch-nexuspci/uncompress.h | 66 ----------- include/asm-arm/arch-nexuspci/vmalloc.h | 15 --- 19 files changed, 4 insertions(+), 784 deletions(-) delete mode 100644 arch/arm/common/plx90x0.c delete mode 100644 arch/arm/mach-ftvpci/Makefile delete mode 100644 arch/arm/mach-ftvpci/core.c delete mode 100644 arch/arm/mach-ftvpci/leds.c delete mode 100644 arch/arm/mach-ftvpci/pci.c delete mode 100644 include/asm-arm/arch-nexuspci/dma.h delete mode 100644 include/asm-arm/arch-nexuspci/hardware.h delete mode 100644 include/asm-arm/arch-nexuspci/io.h delete mode 100644 include/asm-arm/arch-nexuspci/irqs.h delete mode 100644 include/asm-arm/arch-nexuspci/memory.h delete mode 100644 include/asm-arm/arch-nexuspci/param.h delete mode 100644 include/asm-arm/arch-nexuspci/system.h delete mode 100644 include/asm-arm/arch-nexuspci/time.h delete mode 100644 include/asm-arm/arch-nexuspci/timex.h delete mode 100644 include/asm-arm/arch-nexuspci/uncompress.h delete mode 100644 include/asm-arm/arch-nexuspci/vmalloc.h (limited to 'include') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index ff2a826db9e5..a9a5535fffa9 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -257,7 +257,7 @@ config DISCONTIGMEM # Now handle the bus types config PCI bool "PCI support" if ARCH_INTEGRATOR_AP - default y if ARCH_FTVPCI || ARCH_SHARK || FOOTBRIDGE_HOST || ARCH_IOP3XX || ARCH_IXP4XX + default y if ARCH_SHARK || FOOTBRIDGE_HOST || ARCH_IOP3XX || ARCH_IXP4XX help Find out whether you have a PCI motherboard. PCI is the name of a bus system, i.e. the way the CPU talks to the other stuff inside @@ -270,11 +270,6 @@ config PCI doesn't. # Select the host bridge type -config PCI_HOST_PLX90X0 - bool - depends on PCI && ARCH_FTVPCI - default y - config PCI_HOST_VIA82C505 bool depends on PCI && ARCH_SHARK @@ -550,7 +545,7 @@ config CMDLINE config LEDS bool "Timer and CPU usage LEDs" - depends on ARCH_NETWINDER || ARCH_EBSA110 || ARCH_EBSA285 || ARCH_FTVPCI || ARCH_SHARK || ARCH_CO285 || ARCH_SA1100 || ARCH_LUBBOCK || MACH_MAINSTONE || ARCH_PXA_IDP || ARCH_INTEGRATOR || ARCH_CDB89712 || ARCH_P720T || ARCH_OMAP || ARCH_VERSATILE_PB + depends on ARCH_NETWINDER || ARCH_EBSA110 || ARCH_EBSA285 || ARCH_SHARK || ARCH_CO285 || ARCH_SA1100 || ARCH_LUBBOCK || MACH_MAINSTONE || ARCH_PXA_IDP || ARCH_INTEGRATOR || ARCH_CDB89712 || ARCH_P720T || ARCH_OMAP || ARCH_VERSATILE_PB help If you say Y here, the LEDs on your machine will be used to provide useful information about your current system status. @@ -564,7 +559,7 @@ config LEDS config LEDS_TIMER bool "Timer LED" if LEDS && (ARCH_NETWINDER || ARCH_EBSA285 || ARCH_SHARK || MACH_MAINSTONE || ARCH_CO285 || ARCH_SA1100 || ARCH_LUBBOCK || ARCH_PXA_IDP || ARCH_INTEGRATOR || ARCH_P720T || ARCH_VERSATILE_PB) - depends on ARCH_NETWINDER || ARCH_EBSA110 || ARCH_EBSA285 || ARCH_FTVPCI || ARCH_SHARK || ARCH_CO285 || ARCH_SA1100 || ARCH_LUBBOCK || MACH_MAINSTONE || ARCH_PXA_IDP || ARCH_INTEGRATOR || ARCH_CDB89712 || ARCH_P720T || ARCH_OMAP || ARCH_VERSATILE_PB + depends on ARCH_NETWINDER || ARCH_EBSA110 || ARCH_EBSA285 || ARCH_SHARK || ARCH_CO285 || ARCH_SA1100 || ARCH_LUBBOCK || MACH_MAINSTONE || ARCH_PXA_IDP || ARCH_INTEGRATOR || ARCH_CDB89712 || ARCH_P720T || ARCH_OMAP || ARCH_VERSATILE_PB default y if ARCH_EBSA110 help If you say Y here, one of the system LEDs (the green one on the @@ -620,7 +615,7 @@ source "drivers/acorn/block/Kconfig" source "net/Kconfig" -if ARCH_CLPS7500 || ARCH_IOP3XX || ARCH_IXP4XX || ARCH_L7200 || ARCH_LH7A40X || ARCH_FTVPCI || ARCH_PXA || ARCH_RPC || ARCH_S3C2410 || ARCH_SA1100 || ARCH_SHARK || FOOTBRIDGE +if ARCH_CLPS7500 || ARCH_IOP3XX || ARCH_IXP4XX || ARCH_L7200 || ARCH_LH7A40X || ARCH_PXA || ARCH_RPC || ARCH_S3C2410 || ARCH_SA1100 || ARCH_SHARK || FOOTBRIDGE source "drivers/ide/Kconfig" endif diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 2f79fb2a3ed6..954004064480 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -74,8 +74,6 @@ textaddr-y := 0xC0008000 textaddr-$(CONFIG_ARCH_CO285) := 0x60008000 machine-$(CONFIG_ARCH_CO285) := footbridge incdir-$(CONFIG_ARCH_CO285) := ebsa285 - machine-$(CONFIG_ARCH_FTVPCI) := ftvpci - incdir-$(CONFIG_ARCH_FTVPCI) := nexuspci machine-$(CONFIG_ARCH_SHARK) := shark machine-$(CONFIG_ARCH_SA1100) := sa1100 ifeq ($(CONFIG_ARCH_SA1100),y) diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index f326bac694fd..72a12c24689f 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -6,6 +6,5 @@ obj-y += platform.o obj-$(CONFIG_ARM_AMBA) += amba.o obj-$(CONFIG_ICST525) += icst525.o obj-$(CONFIG_SA1111) += sa1111.o -obj-$(CONFIG_PCI_HOST_PLX90X0) += plx90x0.o obj-$(CONFIG_PCI_HOST_VIA82C505) += via82c505.o obj-$(CONFIG_DMABOUNCE) += dmabounce.o diff --git a/arch/arm/common/plx90x0.c b/arch/arm/common/plx90x0.c deleted file mode 100644 index 60d7d3566af4..000000000000 --- a/arch/arm/common/plx90x0.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Driver for PLX Technology PCI9000-series host bridge. - * - * Copyright (C) 1997, 1998, 1999, 2000 FutureTV Labs Ltd - */ - -/* - * 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 - -/* - * Since the following functions are all very similar, the common parts - * are pulled out into these macros. - */ - -#define PLX_CLEAR_CONFIG \ - __raw_writel(0, PLX_BASE + 0xac); \ - local_irq_restore(flags); } - -#define PLX_SET_CONFIG \ - { unsigned long flags; \ - local_irq_save(flags); \ - __raw_writel((1<<31 | (bus->number << 16) \ - | (devfn << 8) | (where & ~3) \ - | ((bus->number == 0)?0:1)), PLX_BASE + 0xac); \ - -#define PLX_CONFIG_WRITE(size) \ - PLX_SET_CONFIG \ - __raw_write##size(value, PCIO_BASE + (where & 3)); \ - if (__raw_readw(PLX_BASE + 0x6) & 0x2000) \ - __raw_writew(0x2000, PLX_BASE + 0x6); \ - PLX_CLEAR_CONFIG \ - return PCIBIOS_SUCCESSFUL; - -#define PLX_CONFIG_READ(size) \ - PLX_SET_CONFIG \ - *value = __raw_read##size(PCIO_BASE + (where & 3)); \ - if (__raw_readw(PLX_BASE + 0x6) & 0x2000) { \ - __raw_writew(0x2000, PLX_BASE + 0x6); \ - *value = 0xffffffffUL; \ - } \ - PLX_CLEAR_CONFIG \ - return PCIBIOS_SUCCESSFUL; - -/* Configuration space access routines */ - -static int -plx90x0_read_config (struct pci_bus *bus, unsigned int devfn, int where, - int where, int size, u32 *value) -{ - switch (size) { - case 1: - PLX_CONFIG_READ(b) - break; - case 2: - PLX_CONFIG_READ(w) - break; - case 4: - PLX_CONFIG_READ(l) - break; - } - return PCIBIOS_SUCCESSFUL; -} - -static int -plx90x0_write_config (struct pci_bus *bus, unsigned int devfn, int where, - int where, int size, u32 value) -{ - switch (size) { - case 1: - PLX_CONFIG_WRITE(b) - break; - case 2: - PLX_CONFIG_WRITE(w) - break; - case 4: - PLX_CONFIG_WRITE(l) - break; - } - return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops plx90x0_ops = -{ - .read = plx90x0_read_config, - .write = plx90x0_write_config, -}; - -static void -plx_syserr_handler(int irq, void *handle, struct pt_regs *regs) -{ - printk("PLX90x0: machine check %04x (pc=%08lx)\n", - readw(PLX_BASE + 6), regs->ARM_pc); - __raw_writew(0xf000, PLX_BASE + 6); -} - -/* - * Initialise the PCI system. - */ - -void __init -plx90x0_init(struct arm_sysdata *sysdata) -{ - static const unsigned long int base = PLX_BASE; - char *what; - unsigned long bar = (unsigned long)virt_to_bus((void *)PAGE_OFFSET); - - /* Have a sniff around and see which PLX device is present. */ - unsigned long id = __raw_readl(base + 0xf0); - -#if 0 - /* This check was a good idea, but can fail. The PLX9060 puts no - default value in these registers unless NB# is asserted (which it - isn't on these cards). */ - if ((id & 0xffff) != PCI_VENDOR_ID_PLX) - return; /* Nothing found */ -#endif - - /* Found one - now work out what it is. */ - switch (id >> 16) { - case 0: /* PCI_DEVICE_ID_PLX_9060 */ - what = "PCI9060"; - break; - case PCI_DEVICE_ID_PLX_9060ES: - what = "PCI9060ES"; - break; - case PCI_DEVICE_ID_PLX_9060SD: - what = "PCI9060SD"; /* uhuhh.. */ - break; - case PCI_DEVICE_ID_PLX_9080: - what = "PCI9080"; - break; - default: - printk("PCI: Unknown PLX device %04lx found -- ignored.\n", - id >> 16); - return; - } - - printk("PCI: PLX Technology %s host bridge found.\n", what); - - /* Now set it up for both master and slave accesses. */ - __raw_writel(0xffff0147, base + 0x4); - __raw_writeb(32, base + 0xd); - __raw_writel(0x8 | bar, base + 0x18); - __raw_writel(0xf8000008, base + 0x80); - __raw_writel(0x40000001, base + 0x84); - __raw_writel(0, base + 0x88); - __raw_writel(0, base + 0x8c); - __raw_writel(0x11, base + 0x94); - __raw_writel(0xC3 + (4 << 28) - + (8 << 11) + (1 << 10) - + (1 << 24), base + 0x98); - __raw_writel(0xC0000000, base + 0x9c); - __raw_writel(PLX_MEM_START, base + 0xa0); - __raw_writel(PLX_IO_START, base + 0xa4); - __raw_writel(0x3, base + 0xa8); - __raw_writel(0, base + 0xac); - __raw_writel(0x10001, base + 0xe8); - __raw_writel(0x8000767e, base + 0xec); - - request_irq(IRQ_SYSERR, plx_syserr_handler, 0, - "system error", NULL); - - pci_scan_bus(0, &plx90x0_ops, sysdata); -} diff --git a/arch/arm/mach-ftvpci/Makefile b/arch/arm/mach-ftvpci/Makefile deleted file mode 100644 index 8b1ad14a256c..000000000000 --- a/arch/arm/mach-ftvpci/Makefile +++ /dev/null @@ -1,13 +0,0 @@ -# -# Makefile for the linux kernel. -# - -# Object file lists. - -obj-y := core.o -obj-m := -obj-n := -obj- := - -obj-$(CONFIG_PCI) += pci.o -obj-$(CONFIG_LEDS) += leds.o diff --git a/arch/arm/mach-ftvpci/core.c b/arch/arm/mach-ftvpci/core.c deleted file mode 100644 index ea6d5c34e87f..000000000000 --- a/arch/arm/mach-ftvpci/core.c +++ /dev/null @@ -1,96 +0,0 @@ -/* - * linux/arch/arm/mach-ftvpci/core.c - * - * Architecture specific fixups. - * - * 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 -#include - -extern unsigned long soft_irq_mask; - -static const unsigned char irq_cmd[] = -{ - INTCONT_IRQ_DUART, - INTCONT_IRQ_PLX, - INTCONT_IRQ_D, - INTCONT_IRQ_C, - INTCONT_IRQ_B, - INTCONT_IRQ_A, - INTCONT_IRQ_SYSERR -}; - -static void ftvpci_mask_irq(unsigned int irq) -{ - __raw_writel(irq_cmd[irq], INTCONT_BASE); - soft_irq_mask &= ~(1<= FIRST_IRQ && i <= LAST_IRQ) { - irq_desc[i].valid = 1; - irq_desc[i].probe_ok = 1; - irq_desc[i].mask_ack = ftvpci_mask_irq; - irq_desc[i].mask = ftvpci_mask_irq; - irq_desc[i].unmask = ftvpci_unmask_irq; - ftvpci_mask_irq(i); - } else { - irq_desc[i].valid = 0; - irq_desc[i].probe_ok = 0; - } - } -} - -static struct map_desc ftvpci_io_desc[] __initdata = { - { INTCONT_BASE, INTCONT_START, 0x00001000, MT_DEVICE }, - { PLX_BASE, PLX_START, 0x00001000, MT_DEVICE }, - { PCIO_BASE, PLX_IO_START, 0x00100000, MT_DEVICE }, - { DUART_BASE, DUART_START, 0x00001000, MT_DEVICE }, - { STATUS_BASE, STATUS_START, 0x00001000, MT_DEVICE } -}; - -static void __init ftvpci_map_io(void) -{ - iotable_init(ftvpci_io_desc, ARRAY_SIZE(ftvpci_io_desc)); -} - -MACHINE_START(NEXUSPCI, "FTV/PCI") - MAINTAINER("Philip Blundell") - BOOT_MEM(0x40000000, 0x10000000, 0xe0000000) - MAPIO(ftvpci_map_io) - INITIRQ(ftvpci_init_irq) -MACHINE_END diff --git a/arch/arm/mach-ftvpci/leds.c b/arch/arm/mach-ftvpci/leds.c deleted file mode 100644 index 64345acb9866..000000000000 --- a/arch/arm/mach-ftvpci/leds.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * linux/arch/arm/kernel/leds-ftvpci.c - * - * Copyright (C) 1999 FutureTV Labs Ltd - */ - -#include -#include - -#include -#include -#include -#include - -static void ftvpci_leds_event(led_event_t ledevt) -{ - static int led_state = 0; - - switch(ledevt) { - case led_timer: - led_state ^= 1; - raw_writeb(0x1a | led_state, INTCONT_BASE); - break; - - default: - break; - } -} - -static int __init ftvpci_leds_init(void) -{ - leds_event = ftvpci_leds_event; - return 0; -} - -arch_initcall(ftvpci_leds_init); diff --git a/arch/arm/mach-ftvpci/pci.c b/arch/arm/mach-ftvpci/pci.c deleted file mode 100644 index a9941a1e46e4..000000000000 --- a/arch/arm/mach-ftvpci/pci.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * linux/arch/arm/kernel/ftv-pci.c - * - * PCI bios-type initialisation for PCI machines - * - * Bits taken from various places. - */ -#include -#include -#include - -#include -#include -#include - -/* - * Owing to a PCB cockup, issue A backplanes are wired thus: - * - * Slot 1 2 3 4 5 Bridge S1 S2 S3 S4 - * IRQ D C B A A C B A D - * A D C B B D C B A - * B A D C C A D C B - * C B A D D B A D C - * - * ID A31 A30 A29 A28 A27 A26 DEV4 DEV5 DEV6 DEV7 - * - * Actually, this isn't too bad, because with the processor card - * in slot 5 on the primary bus, the IRQs rotate on both sides - * as you'd expect. - */ - -static int irqmap_ftv[] __initdata = { IRQ_PCI_D, IRQ_PCI_C, IRQ_PCI_B, IRQ_PCI_A }; - -static int __init ftv_map_irq(struct pci_dev *dev, u8 slot, u8 pin) -{ - if (slot > 0x10) - slot--; - return irqmap_ftv[(slot - pin) & 3]; -} - -static u8 __init ftv_swizzle(struct pci_dev *dev, u8 *pin) -{ - return PCI_SLOT(dev->devfn); -} - -/* ftv host-specific stuff */ -static struct hw_pci ftv_pci __initdata = { - .init = plx90x0_init, - .swizzle = ftv_swizzle, - .map_irq = ftv_map_irq, -}; - -static int __init ftv_pci_init(void) -{ - if (machine_is_ftvpci()) - pci_common_init(&ftv_pci); - return 0; -} - -subsys_initcall(ftv_pci_init); diff --git a/include/asm-arm/arch-nexuspci/dma.h b/include/asm-arm/arch-nexuspci/dma.h deleted file mode 100644 index dee1ea2175c4..000000000000 --- a/include/asm-arm/arch-nexuspci/dma.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/dma.h - * - * Architecture DMA routines - * - * Copyright (C) 1998, 1999 Philip Blundell - */ - -/* - * 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 is the maximum DMA address that can be DMAd to. - */ -#define MAX_DMA_ADDRESS 0xffffffff -#define MAX_DMA_CHANNELS 0 diff --git a/include/asm-arm/arch-nexuspci/hardware.h b/include/asm-arm/arch-nexuspci/hardware.h deleted file mode 100644 index 303dc2c974ba..000000000000 --- a/include/asm-arm/arch-nexuspci/hardware.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/hardware.h - * - * Copyright (C) 1998, 1999, 2000 FutureTV Labs Ltd. - * - * This file contains the hardware definitions of the FTV PCI card. - */ - -/* - * 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 __ASM_ARCH_HARDWARE_H -#define __ASM_ARCH_HARDWARE_H - -/* Logical Physical - * 0xffe00000 0x20000000 INTCONT - * 0xffd00000 0x30000000 Status - * 0xffc00000 0x60000000 PLX registers - * 0xfe000000 0xC0000000 PCI I/O - * 0xfd000000 0x70000000 cache flush - * 0xfc000000 0x80000000 PCI/ISA memory - * 0xe0000000 0x10000000 SCC2691 DUART - */ - -/* - * Mapping areas - */ -#define INTCONT_BASE 0xffe00000 -#define STATUS_BASE 0xffd00000 -#define PLX_BASE 0xffc00000 -#define PCIO_BASE 0xfe000000 -#define FLUSH_BASE 0xfd000000 -#define DUART_BASE 0xe0000000 -#define PCIMEM_BASE 0xfc000000 - -#define PLX_IO_START 0xC0000000 -#define PLX_MEM_START 0x80000000 -#define PLX_START 0x60000000 -#define STATUS_START 0x30000000 -#define INTCONT_START 0x20000000 -#define DUART_START 0x10000000 - -/* - * RAM definitions - */ -#define RAM_BASE 0x40000000 -#define FLUSH_BASE_PHYS 0x70000000 - -/* - * Miscellaneous INTCONT bits - */ -#define INTCONT_FIQ_PLX 0x00 -#define INTCONT_FIQ_D 0x02 -#define INTCONT_FIQ_C 0x04 -#define INTCONT_FIQ_B 0x06 -#define INTCONT_FIQ_A 0x08 -#define INTCONT_FIQ_SYSERR 0x0a -#define INTCONT_IRQ_DUART 0x0c -#define INTCONT_IRQ_PLX 0x0e -#define INTCONT_IRQ_D 0x10 -#define INTCONT_IRQ_C 0x12 -#define INTCONT_IRQ_B 0x14 -#define INTCONT_IRQ_A 0x16 -#define INTCONT_IRQ_SYSERR 0x1e - -#define INTCONT_WATCHDOG 0x18 -#define INTCONT_LED 0x1a -#define INTCONT_PCI_RESET 0x1c - -#define UNCACHEABLE_ADDR STATUS_BASE - -#endif diff --git a/include/asm-arm/arch-nexuspci/io.h b/include/asm-arm/arch-nexuspci/io.h deleted file mode 100644 index 181bdb5988df..000000000000 --- a/include/asm-arm/arch-nexuspci/io.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/io.h - * - * Copyright (C) 1997-1999 Russell King - * Copyright (C) 2000 FutureTV Labs Ltd. - */ -#ifndef __ASM_ARM_ARCH_IO_H -#define __ASM_ARM_ARCH_IO_H - -#define IO_SPACE_LIMIT 0xffff - -/* - * Translation of various region addresses to virtual addresses - */ -#define __io(a) (PCIO_BASE + (a)) -#if 1 -#define __mem_pci(a) ((unsigned long)(a)) -#define __mem_isa(a) (PCIMEM_BASE + (unsigned long)(a)) -#else - -static inline unsigned long ___mem_pci(unsigned long a) -{ - /* PCI addresses must have been ioremapped */ - if (a <= 0xc0000000 || a >= 0xe0000000) - *((int *)0) = 0; - return a; -} - -static inline unsigned long ___mem_isa(unsigned long a) -{ - BUG_ON(a >= 16*1048576); - return PCIMEM_BASE + a; -} -#define __mem_pci(a) ___mem_pci((unsigned long)(a)) -#define __mem_isa(a) ___mem_isa((unsigned long)(a)) -#endif - -/* - * ioremap support - validate a PCI memory address, - * and convert a PCI memory address to a physical - * address for the page tables. - */ -#define iomem_valid_addr(iomem,sz) \ - ((iomem) < 0x80000000 && (iomem) + (sz) <= 0x80000000) -#define iomem_to_phys(iomem) ((iomem) + PLX_MEM_START) - -#define __arch_ioremap(off,sz,nocache) \ - ({ \ - unsigned long _off = (off), _size = (sz); \ - void *_ret = (void *)0; \ - if (iomem_valid_addr(_off, _size)) \ - _ret = __ioremap(iomem_to_phys(_off),_size,0); \ - _ret; \ - }) - -#define __arch_iounmap __iounmap - -#endif diff --git a/include/asm-arm/arch-nexuspci/irqs.h b/include/asm-arm/arch-nexuspci/irqs.h deleted file mode 100644 index 006c14b96b36..000000000000 --- a/include/asm-arm/arch-nexuspci/irqs.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/irqs.h - * - * Copyright (C) 1997, 1998, 2000 Philip Blundell - */ - -/* - * 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. - */ - -/* - * The hardware is capable of routing any interrupt source (except the - * DUART) to either IRQ or FIQ. We ignore FIQ and use IRQ exclusively - * for simplicity. - */ - -#define IRQ_DUART 0 -#define IRQ_PLX 1 -#define IRQ_PCI_D 2 -#define IRQ_PCI_C 3 -#define IRQ_PCI_B 4 -#define IRQ_PCI_A 5 -#define IRQ_SYSERR 6 /* only from IOSLAVE rev B */ - -#define FIRST_IRQ IRQ_DUART -#define LAST_IRQ IRQ_SYSERR - -/* timer is part of the DUART */ -#define IRQ_TIMER IRQ_DUART - -#define irq_canonicalize(i) (i) diff --git a/include/asm-arm/arch-nexuspci/memory.h b/include/asm-arm/arch-nexuspci/memory.h deleted file mode 100644 index 85d7c45055f7..000000000000 --- a/include/asm-arm/arch-nexuspci/memory.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/memory.h - * - * Copyright (c) 1997, 1998, 2000 FutureTV Labs Ltd. - * Copyright (c) 1999 Russell King - * - */ -#ifndef __ASM_ARCH_MEMORY_H -#define __ASM_ARCH_MEMORY_H - -/* - * Physical DRAM offset. - */ -#define PHYS_OFFSET (0x40000000UL) -#define BUS_OFFSET (0xe0000000UL) - -/* - * On the PCI bus the DRAM appears at address 0xe0000000 - */ -#define __virt_to_bus(x) ((unsigned long)(x) - PAGE_OFFSET + BUS_OFFSET) -#define __bus_to_virt(x) ((unsigned long)(x) + PAGE_OFFSET - BUS_OFFSET) - -#endif diff --git a/include/asm-arm/arch-nexuspci/param.h b/include/asm-arm/arch-nexuspci/param.h deleted file mode 100644 index d57753d8f1b8..000000000000 --- a/include/asm-arm/arch-nexuspci/param.h +++ /dev/null @@ -1,3 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/param.h - */ diff --git a/include/asm-arm/arch-nexuspci/system.h b/include/asm-arm/arch-nexuspci/system.h deleted file mode 100644 index 824a1d7ad403..000000000000 --- a/include/asm-arm/arch-nexuspci/system.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/system.h - * - * Copyright (c) 1996, 97, 98, 99, 2000 FutureTV Labs Ltd. - */ - -/* - * 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 __ASM_ARCH_SYSTEM_H -#define __ASM_ARCH_SYSTEM_H - -static inline void arch_idle(void) -{ - cpu_do_idle(); -} - -#define arch_reset(mode) do { } while (0) - -#endif diff --git a/include/asm-arm/arch-nexuspci/time.h b/include/asm-arm/arch-nexuspci/time.h deleted file mode 100644 index c0fd0cdc9cb0..000000000000 --- a/include/asm-arm/arch-nexuspci/time.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/time.h - * - * Copyright (c) 1997, 1998, 1999, 2000 FutureTV Labs Ltd. - * - * The FTV PCI card has no real-time clock. We get timer ticks from the - * SCC chip. - */ - -/* - * 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. - */ - -static irqreturn_t -timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) -{ - static int count = 25; - unsigned char stat = __raw_readb(DUART_BASE + 0x14); - if (!(stat & 0x10)) - return; /* Not for us */ - - /* Reset counter */ - __raw_writeb(0x90, DUART_BASE + 8); - - if (--count == 0) { - static int state = 1; - state ^= 1; - __raw_writeb(0x1a + state, INTCONT_BASE); - __raw_writeb(0x18 + state, INTCONT_BASE); - count = 50; - } - - /* Wait for slow rise time */ - __raw_readb(DUART_BASE + 0x14); - __raw_readb(DUART_BASE + 0x14); - __raw_readb(DUART_BASE + 0x14); - __raw_readb(DUART_BASE + 0x14); - __raw_readb(DUART_BASE + 0x14); - __raw_readb(DUART_BASE + 0x14); - - do_timer(regs); - - return IRQ_HANDLED; -} - -void __init time_init(void) -{ - int tick = 3686400 / 16 / 2 / 100; - - __raw_writeb(tick & 0xff, DUART_BASE + 0x1c); - __raw_writeb(tick >> 8, DUART_BASE + 0x18); - __raw_writeb(0x80, DUART_BASE + 8); - __raw_writeb(0x10, DUART_BASE + 0x14); - - timer_irq.handler = timer_interrupt; - timer_irq.flags = SA_SHIRQ; - - setup_irq(IRQ_TIMER, &timer_irq); -} diff --git a/include/asm-arm/arch-nexuspci/timex.h b/include/asm-arm/arch-nexuspci/timex.h deleted file mode 100644 index 63d4e2cd1938..000000000000 --- a/include/asm-arm/arch-nexuspci/timex.h +++ /dev/null @@ -1,8 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/timex.h - * - * NexusPCI StrongARM card timex specifications - * - * Copyright (C) 1998 Philip Blundell - */ - diff --git a/include/asm-arm/arch-nexuspci/uncompress.h b/include/asm-arm/arch-nexuspci/uncompress.h deleted file mode 100644 index dd697a92466c..000000000000 --- a/include/asm-arm/arch-nexuspci/uncompress.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/uncompress.h - * - * Copyright (C) 1998, 1999, 2000 Philip Blundell - */ - -/* - * 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 - -/* - * Write a character to the UART - */ -void _ll_write_char(char c) -{ - while (!(__raw_readb(DUART_START + 0x4) & 0x4)) - ; - __raw_writeb(c, DUART_START + 0xc); -} - -/* - * This does not append a newline - */ -static void puts(const char *s) -{ - while (*s) { - if (*s == '\n') - _ll_write_char('\r'); - _ll_write_char(*(s++)); - } -} - -/* - * Set up for decompression - */ -static void arch_decomp_setup(void) -{ - /* LED off */ - __raw_writel(INTCONT_LED, INTCONT_START); - - /* Set up SCC */ - __raw_writeb(42, DUART_START + 8); - __raw_writeb(48, DUART_START + 8); - __raw_writeb(16, DUART_START + 8); - __raw_writeb(0x93, DUART_START); - __raw_writeb(0x17, DUART_START); - __raw_writeb(0xbb, DUART_START + 4); - __raw_writeb(0x78, DUART_START + 16); - __raw_writeb(0xa0, DUART_START + 8); - __raw_writeb(5, DUART_START + 8); -} - -/* - * Stroke the watchdog so we don't get reset during decompression. - */ -static inline void arch_decomp_wdog(void) -{ - __raw_writel(INTCONT_WATCHDOG, INTCONT_START); - __raw_writel(INTCONT_WATCHDOG | 1, INTCONT_START); -} diff --git a/include/asm-arm/arch-nexuspci/vmalloc.h b/include/asm-arm/arch-nexuspci/vmalloc.h deleted file mode 100644 index 7e93df2307fc..000000000000 --- a/include/asm-arm/arch-nexuspci/vmalloc.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * linux/include/asm-arm/arch-nexuspci/vmalloc.h - */ - -/* - * Just any arbitrary offset to the start of the vmalloc VM area: the - * current 8MB value just means that there will be a 8MB "hole" after the - * physical memory until the kernel virtual memory starts. That means that - * any out-of-bounds memory accesses will hopefully be caught. - * The vmalloc() routines leaves a hole of 4kB between each vmalloced - * area for the same reason. ;) - */ -#define VMALLOC_OFFSET (8*1024*1024) -#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)) -#define VMALLOC_END (PAGE_OFFSET + 0x20000000) -- cgit v1.2.3 From 0d51db7b357ffd86243859acb4c0cb9ae1bfd4db Mon Sep 17 00:00:00 2001 From: Hideaki Yoshifuji Date: Sat, 19 Jun 2004 08:25:15 +0900 Subject: [IPV6] XFRM: support (uncompressed) tunnel mode ipcomp6 using xfrm6_tunnel infrastructure. --- include/net/xfrm.h | 18 +- net/ipv6/Makefile | 3 +- net/ipv6/ip6_tunnel.c | 18 +- net/ipv6/ipcomp6.c | 69 ++++++ net/ipv6/xfrm6_policy.c | 2 + net/ipv6/xfrm6_tunnel.c | 623 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 719 insertions(+), 14 deletions(-) create mode 100644 net/ipv6/xfrm6_tunnel.c (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 64704968507f..d718cb222187 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -497,10 +497,6 @@ xfrm_selector_match(struct xfrm_selector *sel, struct flowi *fl, return 0; } -/* placeholder until xfrm6_tunnel.c is written */ -static inline int xfrm6_tunnel_check_size(struct sk_buff *skb) -{ return 0; } - /* A struct encoding bundle of transformations to apply to some set of flow. * * dst->child points to the next element of bundle. @@ -783,6 +779,12 @@ struct xfrm_tunnel { void (*err_handler)(struct sk_buff *skb, void *info); }; +struct xfrm6_tunnel { + int (*handler)(struct sk_buff **pskb, unsigned int *nhoffp); + void (*err_handler)(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info); +}; + extern void xfrm_init(void); extern void xfrm4_init(void); extern void xfrm4_fini(void); @@ -793,6 +795,8 @@ extern void xfrm4_state_init(void); extern void xfrm4_state_fini(void); extern void xfrm6_state_init(void); extern void xfrm6_state_fini(void); +extern void xfrm6_tunnel_init(void); +extern void xfrm6_tunnel_fini(void); extern int xfrm_state_walk(u8 proto, int (*func)(struct xfrm_state *, int, void*), void *); extern struct xfrm_state *xfrm_state_alloc(void); @@ -818,6 +822,12 @@ extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler); extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler); extern int xfrm4_tunnel_check_size(struct sk_buff *skb); extern int xfrm6_rcv(struct sk_buff **pskb, unsigned int *nhoffp); +extern int xfrm6_tunnel_register(struct xfrm6_tunnel *handler); +extern int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler); +extern int xfrm6_tunnel_check_size(struct sk_buff *skb); +extern u32 xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr); +extern void xfrm6_tunnel_free_spi(xfrm_address_t *saddr); +extern u32 xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr); #ifdef CONFIG_XFRM extern int xfrm4_rcv_encap(struct sk_buff *skb, __u16 encap_type); diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 26e292c9db9e..31afc9cfc0a6 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -10,7 +10,8 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \ exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ ip6_flowlabel.o ipv6_syms.o -ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o +ipv6-$(CONFIG_XFRM) += xfrm6_policy.o xfrm6_state.o xfrm6_input.o \ + xfrm6_tunnel.o ipv6-objs += $(ipv6-y) obj-$(CONFIG_INET6_AH) += ah6.o diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 56a626178061..9e5677dd45a2 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -540,8 +540,7 @@ ip6ip6_rcv(struct sk_buff **pskb, unsigned int *nhoffp) read_unlock(&ip6ip6_lock); icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev); discard: - kfree_skb(skb); - return 0; + return 1; } static inline struct ipv6_txoptions *create_tel(__u8 encap_limit) @@ -1097,10 +1096,9 @@ ip6ip6_fb_tnl_dev_init(struct net_device *dev) return 0; } -static struct inet6_protocol ip6ip6_protocol = { +static struct xfrm6_tunnel ip6ip6_handler = { .handler = ip6ip6_rcv, .err_handler = ip6ip6_err, - .flags = INET6_PROTO_FINAL }; /** @@ -1113,9 +1111,9 @@ static int __init ip6_tunnel_init(void) { int err; - if ((err = inet6_add_protocol(&ip6ip6_protocol, IPPROTO_IPV6)) < 0) { - printk(KERN_ERR "Failed to register IPv6 protocol\n"); - return err; + if (xfrm6_tunnel_register(&ip6ip6_handler) < 0) { + printk(KERN_ERR "ip6ip6 init: can't register tunnel\n"); + return -EAGAIN; } ip6ip6_fb_tnl_dev = alloc_netdev(sizeof(struct ip6_tnl), "ip6tnl0", ip6ip6_tnl_dev_setup); @@ -1132,7 +1130,7 @@ static int __init ip6_tunnel_init(void) } return 0; fail: - inet6_del_protocol(&ip6ip6_protocol, IPPROTO_IPV6); + xfrm6_tunnel_deregister(&ip6ip6_handler); return err; } @@ -1142,8 +1140,10 @@ fail: static void __exit ip6_tunnel_cleanup(void) { + if (xfrm6_tunnel_deregister(&ip6ip6_handler) < 0) + printk(KERN_INFO "ip6ip6 close: can't deregister tunnel\n"); + unregister_netdev(ip6ip6_fb_tnl_dev); - inet6_del_protocol(&ip6ip6_protocol, IPPROTO_IPV6); } module_init(ip6_tunnel_init); diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index b357db4cc84b..18d0f79dffa7 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -258,6 +258,66 @@ static void ipcomp6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, xfrm_state_put(x); } +static struct xfrm_state *ipcomp6_tunnel_create(struct xfrm_state *x) +{ + struct xfrm_state *t = NULL; + + t = xfrm_state_alloc(); + if (!t) + goto out; + + t->id.proto = IPPROTO_IPV6; + t->id.spi = xfrm6_tunnel_alloc_spi((xfrm_address_t *)&x->props.saddr); + memcpy(t->id.daddr.a6, x->id.daddr.a6, sizeof(struct in6_addr)); + memcpy(&t->sel, &x->sel, sizeof(t->sel)); + t->props.family = AF_INET6; + t->props.mode = 1; + memcpy(t->props.saddr.a6, x->props.saddr.a6, sizeof(struct in6_addr)); + + t->type = xfrm_get_type(IPPROTO_IPV6, t->props.family); + if (t->type == NULL) + goto error; + + if (t->type->init_state(t, NULL)) + goto error; + + t->km.state = XFRM_STATE_VALID; + atomic_set(&t->tunnel_users, 1); + +out: + return t; + +error: + xfrm_state_put(t); + goto out; +} + +static int ipcomp6_tunnel_attach(struct xfrm_state *x) +{ + int err = 0; + struct xfrm_state *t = NULL; + u32 spi; + + spi = xfrm6_tunnel_spi_lookup((xfrm_address_t *)&x->props.saddr); + if (spi) + t = xfrm_state_lookup((xfrm_address_t *)&x->id.daddr, + spi, IPPROTO_IPV6, AF_INET6); + if (!t) { + t = ipcomp6_tunnel_create(x); + if (!t) { + err = -EINVAL; + goto out; + } + xfrm_state_insert(t); + xfrm_state_hold(t); + } + x->tunnel = t; + atomic_inc(&t->tunnel_users); + +out: + return err; +} + static void ipcomp6_free_data(struct ipcomp_data *ipcd) { if (ipcd->tfm) @@ -271,8 +331,11 @@ static void ipcomp6_destroy(struct xfrm_state *x) struct ipcomp_data *ipcd = x->data; if (!ipcd) return; + xfrm_state_delete_tunnel(x); ipcomp6_free_data(ipcd); kfree(ipcd); + + xfrm6_tunnel_free_spi((xfrm_address_t *)&x->props.saddr); } static int ipcomp6_init_state(struct xfrm_state *x, void *args) @@ -303,6 +366,12 @@ static int ipcomp6_init_state(struct xfrm_state *x, void *args) if (!ipcd->tfm) goto error; + if (x->props.mode) { + err = ipcomp6_tunnel_attach(x); + if (err) + goto error; + } + calg_desc = xfrm_calg_get_byname(x->calg->alg_name); BUG_ON(!calg_desc); ipcd->threshold = calg_desc->uinfo.comp.threshold; diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 1041ef9524af..b5a3bdb440e4 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -277,10 +277,12 @@ void __init xfrm6_init(void) { xfrm6_policy_init(); xfrm6_state_init(); + xfrm6_tunnel_init(); } void __exit xfrm6_fini(void) { + xfrm6_tunnel_fini(); //xfrm6_input_fini(); xfrm6_policy_fini(); xfrm6_state_fini(); diff --git a/net/ipv6/xfrm6_tunnel.c b/net/ipv6/xfrm6_tunnel.c new file mode 100644 index 000000000000..a5a13c584711 --- /dev/null +++ b/net/ipv6/xfrm6_tunnel.c @@ -0,0 +1,623 @@ +/* + * Copyright (C)2003,2004 USAGI/WIDE 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 + * + * Authors Mitsuru KANDA + * YOSHIFUJI Hideaki + * + * Based on net/ipv4/xfrm4_tunnel.c + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_IPV6_XFRM6_TUNNEL_DEBUG +# define X6TDEBUG 3 +#else +# define X6TDEBUG 1 +#endif + +#define X6TPRINTK(fmt, args...) printk(fmt, ## args) +#define X6TNOPRINTK(fmt, args...) do { ; } while(0) + +#if X6TDEBUG >= 1 +# define X6TPRINTK1 X6TPRINTK +#else +# define X6TPRINTK1 X6TNOPRINTK +#endif + +#if X6TDEBUG >= 3 +# define X6TPRINTK3 X6TPRINTK +#else +# define X6TPRINTK3 X6TNOPRINTK +#endif + +/* + * xfrm_tunnel_spi things are for allocating unique id ("spi") + * per xfrm_address_t. + */ +struct xfrm6_tunnel_spi { + struct hlist_node list_byaddr; + struct hlist_node list_byspi; + xfrm_address_t addr; + u32 spi; + atomic_t refcnt; +#ifdef XFRM6_TUNNEL_SPI_MAGIC + u32 magic; +#endif +}; + +#ifdef CONFIG_IPV6_XFRM6_TUNNEL_DEBUG +# define XFRM6_TUNNEL_SPI_MAGIC 0xdeadbeef +#endif + +static rwlock_t xfrm6_tunnel_spi_lock = RW_LOCK_UNLOCKED; + +static u32 xfrm6_tunnel_spi; + +#define XFRM6_TUNNEL_SPI_MIN 1 +#define XFRM6_TUNNEL_SPI_MAX 0xffffffff + +static kmem_cache_t *xfrm6_tunnel_spi_kmem; + +#define XFRM6_TUNNEL_SPI_BYADDR_HSIZE 256 +#define XFRM6_TUNNEL_SPI_BYSPI_HSIZE 256 + +static struct hlist_head xfrm6_tunnel_spi_byaddr[XFRM6_TUNNEL_SPI_BYADDR_HSIZE]; +static struct hlist_head xfrm6_tunnel_spi_byspi[XFRM6_TUNNEL_SPI_BYSPI_HSIZE]; + +#ifdef XFRM6_TUNNEL_SPI_MAGIC +static int x6spi_check_magic(const struct xfrm6_tunnel_spi *x6spi, + const char *name) +{ + if (unlikely(x6spi->magic != XFRM6_TUNNEL_SPI_MAGIC)) { + X6TPRINTK3(KERN_DEBUG "%s(): x6spi object " + "at %p has corrupted magic %08x " + "(should be %08x)\n", + name, x6spi, x6spi->magic, XFRM6_TUNNEL_SPI_MAGIC); + return -1; + } + return 0; +} +#else +static int inline x6spi_check_magic(const struct xfrm6_tunnel_spi *x6spi, + const char *name) +{ + return 0; +} +#endif + +#define X6SPI_CHECK_MAGIC(x6spi) x6spi_check_magic((x6spi), __FUNCTION__) + + +static unsigned inline xfrm6_tunnel_spi_hash_byaddr(xfrm_address_t *addr) +{ + unsigned h; + + X6TPRINTK3(KERN_DEBUG "%s(addr=%p)\n", __FUNCTION__, addr); + + h = addr->a6[0] ^ addr->a6[1] ^ addr->a6[2] ^ addr->a6[3]; + h ^= h >> 16; + h ^= h >> 8; + h &= XFRM6_TUNNEL_SPI_BYADDR_HSIZE - 1; + + X6TPRINTK3(KERN_DEBUG "%s() = %u\n", __FUNCTION__, h); + + return h; +} + +static unsigned inline xfrm6_tunnel_spi_hash_byspi(u32 spi) +{ + return spi % XFRM6_TUNNEL_SPI_BYSPI_HSIZE; +} + + +static int xfrm6_tunnel_spi_init(void) +{ + int i; + + X6TPRINTK3(KERN_DEBUG "%s()\n", __FUNCTION__); + + xfrm6_tunnel_spi = 0; + xfrm6_tunnel_spi_kmem = kmem_cache_create("xfrm6_tunnel_spi", + sizeof(struct xfrm6_tunnel_spi), + 0, SLAB_HWCACHE_ALIGN, + NULL, NULL); + if (!xfrm6_tunnel_spi_kmem) { + X6TPRINTK1(KERN_ERR + "%s(): failed to allocate xfrm6_tunnel_spi_kmem\n", + __FUNCTION__); + return -ENOMEM; + } + + for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++) + INIT_HLIST_HEAD(&xfrm6_tunnel_spi_byaddr[i]); + for (i = 0; i < XFRM6_TUNNEL_SPI_BYSPI_HSIZE; i++) + INIT_HLIST_HEAD(&xfrm6_tunnel_spi_byspi[i]); + return 0; +} + +static void xfrm6_tunnel_spi_fini(void) +{ + int i; + + X6TPRINTK3(KERN_DEBUG "%s()\n", __FUNCTION__); + + for (i = 0; i < XFRM6_TUNNEL_SPI_BYADDR_HSIZE; i++) { + if (!hlist_empty(&xfrm6_tunnel_spi_byaddr[i])) + goto err; + } + for (i = 0; i < XFRM6_TUNNEL_SPI_BYSPI_HSIZE; i++) { + if (!hlist_empty(&xfrm6_tunnel_spi_byspi[i])) + goto err; + } + kmem_cache_destroy(xfrm6_tunnel_spi_kmem); + xfrm6_tunnel_spi_kmem = NULL; + return; +err: + X6TPRINTK1(KERN_ERR "%s(): table is not empty\n", __FUNCTION__); + return; +} + +static struct xfrm6_tunnel_spi *__xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr) +{ + struct xfrm6_tunnel_spi *x6spi; + struct hlist_node *pos; + + X6TPRINTK3(KERN_DEBUG "%s(saddr=%p)\n", __FUNCTION__, saddr); + + hlist_for_each_entry(x6spi, pos, + &xfrm6_tunnel_spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], + list_byaddr) { + if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) { + X6SPI_CHECK_MAGIC(x6spi); + X6TPRINTK3(KERN_DEBUG "%s() = %p(%u)\n", __FUNCTION__, x6spi, x6spi->spi); + return x6spi; + } + } + + X6TPRINTK3(KERN_DEBUG "%s() = NULL(0)\n", __FUNCTION__); + return NULL; +} + +u32 xfrm6_tunnel_spi_lookup(xfrm_address_t *saddr) +{ + struct xfrm6_tunnel_spi *x6spi; + u32 spi; + + X6TPRINTK3(KERN_DEBUG "%s(saddr=%p)\n", __FUNCTION__, saddr); + + read_lock_bh(&xfrm6_tunnel_spi_lock); + x6spi = __xfrm6_tunnel_spi_lookup(saddr); + spi = x6spi ? x6spi->spi : 0; + read_unlock_bh(&xfrm6_tunnel_spi_lock); + return spi; +} + +static u32 __xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr) +{ + u32 spi; + struct xfrm6_tunnel_spi *x6spi; + struct hlist_node *pos; + unsigned index; + + X6TPRINTK3(KERN_DEBUG "%s(saddr=%p)\n", __FUNCTION__, saddr); + + if (xfrm6_tunnel_spi < XFRM6_TUNNEL_SPI_MIN || + xfrm6_tunnel_spi >= XFRM6_TUNNEL_SPI_MAX) + xfrm6_tunnel_spi = XFRM6_TUNNEL_SPI_MIN; + else + xfrm6_tunnel_spi++; + + for (spi = xfrm6_tunnel_spi; spi <= XFRM6_TUNNEL_SPI_MAX; spi++) { + index = xfrm6_tunnel_spi_hash_byspi(spi); + hlist_for_each_entry(x6spi, pos, + &xfrm6_tunnel_spi_byspi[index], + list_byspi) { + if (x6spi->spi == spi) + goto try_next_1; + } + xfrm6_tunnel_spi = spi; + goto alloc_spi; +try_next_1:; + } + for (spi = XFRM6_TUNNEL_SPI_MIN; spi < xfrm6_tunnel_spi; spi++) { + index = xfrm6_tunnel_spi_hash_byspi(spi); + hlist_for_each_entry(x6spi, pos, + &xfrm6_tunnel_spi_byspi[index], + list_byspi) { + if (x6spi->spi == spi) + goto try_next_2; + } + xfrm6_tunnel_spi = spi; + goto alloc_spi; +try_next_2:; + } + spi = 0; + goto out; +alloc_spi: + X6TPRINTK3(KERN_DEBUG "%s(): allocate new spi for " + "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", + __FUNCTION__, + NIP6(*(struct in6_addr *)saddr)); + x6spi = kmem_cache_alloc(xfrm6_tunnel_spi_kmem, SLAB_ATOMIC); + if (!x6spi) { + X6TPRINTK1(KERN_ERR "%s(): kmem_cache_alloc() failed\n", + __FUNCTION__); + goto out; + } +#ifdef XFRM6_TUNNEL_SPI_MAGIC + x6spi->magic = XFRM6_TUNNEL_SPI_MAGIC; +#endif + memcpy(&x6spi->addr, saddr, sizeof(x6spi->addr)); + x6spi->spi = spi; + atomic_set(&x6spi->refcnt, 1); + + hlist_add_head(&x6spi->list_byspi, &xfrm6_tunnel_spi_byspi[index]); + + index = xfrm6_tunnel_spi_hash_byaddr(saddr); + hlist_add_head(&x6spi->list_byaddr, &xfrm6_tunnel_spi_byaddr[index]); + X6SPI_CHECK_MAGIC(x6spi); +out: + X6TPRINTK3(KERN_DEBUG "%s() = %u\n", __FUNCTION__, spi); + return spi; +} + +u32 xfrm6_tunnel_alloc_spi(xfrm_address_t *saddr) +{ + struct xfrm6_tunnel_spi *x6spi; + u32 spi; + + X6TPRINTK3(KERN_DEBUG "%s(saddr=%p)\n", __FUNCTION__, saddr); + + write_lock_bh(&xfrm6_tunnel_spi_lock); + x6spi = __xfrm6_tunnel_spi_lookup(saddr); + if (x6spi) { + atomic_inc(&x6spi->refcnt); + spi = x6spi->spi; + } else + spi = __xfrm6_tunnel_alloc_spi(saddr); + write_unlock_bh(&xfrm6_tunnel_spi_lock); + + X6TPRINTK3(KERN_DEBUG "%s() = %u\n", __FUNCTION__, spi); + + return spi; +} + +void xfrm6_tunnel_free_spi(xfrm_address_t *saddr) +{ + struct xfrm6_tunnel_spi *x6spi; + struct hlist_node *pos, *n; + + X6TPRINTK3(KERN_DEBUG "%s(saddr=%p)\n", __FUNCTION__, saddr); + + write_lock_bh(&xfrm6_tunnel_spi_lock); + + hlist_for_each_entry_safe(x6spi, pos, n, + &xfrm6_tunnel_spi_byaddr[xfrm6_tunnel_spi_hash_byaddr(saddr)], + list_byaddr) + { + if (memcmp(&x6spi->addr, saddr, sizeof(x6spi->addr)) == 0) { + X6TPRINTK3(KERN_DEBUG "%s(): x6spi object " + "for %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x " + "found at %p\n", + __FUNCTION__, + NIP6(*(struct in6_addr *)saddr), + x6spi); + X6SPI_CHECK_MAGIC(x6spi); + if (atomic_dec_and_test(&x6spi->refcnt)) { + hlist_del(&x6spi->list_byaddr); + hlist_del(&x6spi->list_byspi); + kmem_cache_free(xfrm6_tunnel_spi_kmem, x6spi); + break; + } + } + } + write_unlock_bh(&xfrm6_tunnel_spi_lock); +} + + +int xfrm6_tunnel_check_size(struct sk_buff *skb) +{ + int mtu, ret = 0; + struct dst_entry *dst = skb->dst; + + mtu = dst_pmtu(dst) - sizeof(struct ipv6hdr); + if (mtu < IPV6_MIN_MTU) + mtu = IPV6_MIN_MTU; + + if (skb->len > mtu) { + icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, skb->dev); + ret = -EMSGSIZE; + } + + return ret; +} + +static int xfrm6_tunnel_output(struct sk_buff **pskb) +{ + struct sk_buff *skb = *pskb; + struct dst_entry *dst = skb->dst; + struct xfrm_state *x = dst->xfrm; + struct ipv6hdr *iph, *top_iph; + int err; + + if ((err = xfrm6_tunnel_check_size(skb)) != 0) + goto error_nolock; + + iph = skb->nh.ipv6h; + + top_iph = (struct ipv6hdr *)skb_push(skb, x->props.header_len); + top_iph->version = 6; + top_iph->priority = iph->priority; + top_iph->flow_lbl[0] = iph->flow_lbl[0]; + top_iph->flow_lbl[1] = iph->flow_lbl[1]; + top_iph->flow_lbl[2] = iph->flow_lbl[2]; + top_iph->nexthdr = IPPROTO_IPV6; + top_iph->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); + top_iph->hop_limit = iph->hop_limit; + memcpy(&top_iph->saddr, (struct in6_addr *)&x->props.saddr, sizeof(struct in6_addr)); + memcpy(&top_iph->daddr, (struct in6_addr *)&x->id.daddr, sizeof(struct in6_addr)); + skb->nh.raw = skb->data; + skb->h.raw = skb->nh.raw + sizeof(struct ipv6hdr); + + x->curlft.bytes += skb->len; + x->curlft.packets++; + + spin_unlock_bh(&x->lock); + + if ((skb->dst = dst_pop(dst)) == NULL) { + kfree_skb(skb); + err = -EHOSTUNREACH; + goto error_nolock; + } + + return NET_XMIT_BYPASS; + +error_nolock: + kfree_skb(skb); + return err; +} + +static int xfrm6_tunnel_input(struct xfrm_state *x, struct xfrm_decap_state *decap, struct sk_buff *skb) +{ + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + return -EINVAL; + + skb->mac.raw = skb->nh.raw; + skb->nh.raw = skb->data; + dst_release(skb->dst); + skb->dst = NULL; + skb->protocol = htons(ETH_P_IPV6); + skb->pkt_type = PACKET_HOST; + netif_rx(skb); + + return 0; +} + +static struct xfrm6_tunnel *xfrm6_tunnel_handler; +static DECLARE_MUTEX(xfrm6_tunnel_sem); + +int xfrm6_tunnel_register(struct xfrm6_tunnel *handler) +{ + int ret; + + down(&xfrm6_tunnel_sem); + ret = 0; + if (xfrm6_tunnel_handler != NULL) + ret = -EINVAL; + if (!ret) + xfrm6_tunnel_handler = handler; + up(&xfrm6_tunnel_sem); + + return ret; +} + +int xfrm6_tunnel_deregister(struct xfrm6_tunnel *handler) +{ + int ret; + + down(&xfrm6_tunnel_sem); + ret = 0; + if (xfrm6_tunnel_handler != handler) + ret = -EINVAL; + if (!ret) + xfrm6_tunnel_handler = NULL; + up(&xfrm6_tunnel_sem); + + synchronize_net(); + + return ret; +} + +static int xfrm6_tunnel_rcv(struct sk_buff **pskb, unsigned int *nhoffp) +{ + struct sk_buff *skb = *pskb; + struct xfrm6_tunnel *handler = xfrm6_tunnel_handler; + struct xfrm_state *x = NULL; + struct ipv6hdr *iph = skb->nh.ipv6h; + int err = 0; + u32 spi; + + /* device-like_ip6ip6_handler() */ + if (handler) { + err = handler->handler(pskb, nhoffp); + if (!err) + goto out; + } + + spi = xfrm6_tunnel_spi_lookup((xfrm_address_t *)&iph->saddr); + x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, + spi, + IPPROTO_IPV6, AF_INET6); + + if (!x) + goto drop; + + spin_lock(&x->lock); + + if (unlikely(x->km.state != XFRM_STATE_VALID)) + goto drop_unlock; + + err = xfrm6_tunnel_input(x, NULL, skb); + if (err) + goto drop_unlock; + + x->curlft.bytes += skb->len; + x->curlft.packets++; + spin_unlock(&x->lock); + xfrm_state_put(x); + +out: + return 0; + +drop_unlock: + spin_unlock(&x->lock); + xfrm_state_put(x); +drop: + kfree_skb(skb); + return -1; +} + +static void xfrm6_tunnel_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info) +{ + struct xfrm6_tunnel *handler = xfrm6_tunnel_handler; + + /* call here first for device-like ip6ip6 err handling */ + if (handler) { + handler->err_handler(skb, opt, type, code, offset, info); + return; + } + + /* xfrm6_tunnel native err handling */ + switch (type) { + case ICMPV6_DEST_UNREACH: + switch (code) { + case ICMPV6_NOROUTE: + case ICMPV6_ADM_PROHIBITED: + case ICMPV6_NOT_NEIGHBOUR: + case ICMPV6_ADDR_UNREACH: + case ICMPV6_PORT_UNREACH: + default: + X6TPRINTK3(KERN_DEBUG + "xfrm6_tunnel: Destination Unreach.\n"); + break; + } + break; + case ICMPV6_PKT_TOOBIG: + X6TPRINTK3(KERN_DEBUG + "xfrm6_tunnel: Packet Too Big.\n"); + break; + case ICMPV6_TIME_EXCEED: + switch (code) { + case ICMPV6_EXC_HOPLIMIT: + X6TPRINTK3(KERN_DEBUG + "xfrm6_tunnel: Too small Hoplimit.\n"); + break; + case ICMPV6_EXC_FRAGTIME: + default: + break; + } + break; + case ICMPV6_PARAMPROB: + switch (code) { + case ICMPV6_HDR_FIELD: break; + case ICMPV6_UNK_NEXTHDR: break; + case ICMPV6_UNK_OPTION: break; + } + break; + default: + break; + } + return; +} + +static int xfrm6_tunnel_init_state(struct xfrm_state *x, void *args) +{ + if (!x->props.mode) + return -EINVAL; + + x->props.header_len = sizeof(struct ipv6hdr); + + return 0; +} + +static void xfrm6_tunnel_destroy(struct xfrm_state *x) +{ + xfrm6_tunnel_free_spi((xfrm_address_t *)&x->props.saddr); +} + +static struct xfrm_type xfrm6_tunnel_type = { + .description = "IP6IP6", + .owner = THIS_MODULE, + .proto = IPPROTO_IPV6, + .init_state = xfrm6_tunnel_init_state, + .destructor = xfrm6_tunnel_destroy, + .input = xfrm6_tunnel_input, + .output = xfrm6_tunnel_output, +}; + +static struct inet6_protocol xfrm6_tunnel_protocol = { + .handler = xfrm6_tunnel_rcv, + .err_handler = xfrm6_tunnel_err, + .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, +}; + +void __init xfrm6_tunnel_init(void) +{ + X6TPRINTK3(KERN_DEBUG "%s()\n", __FUNCTION__); + + if (xfrm_register_type(&xfrm6_tunnel_type, AF_INET6) < 0) { + X6TPRINTK1(KERN_ERR + "xfrm6_tunnel init: can't add xfrm type\n"); + return; + } + if (inet6_add_protocol(&xfrm6_tunnel_protocol, IPPROTO_IPV6) < 0) { + X6TPRINTK1(KERN_ERR + "xfrm6_tunnel init(): can't add protocol\n"); + xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6); + return; + } + if (xfrm6_tunnel_spi_init() < 0) { + X6TPRINTK1(KERN_ERR + "xfrm6_tunnel init: failed to initialize spi\n"); + inet6_del_protocol(&xfrm6_tunnel_protocol, IPPROTO_IPV6); + xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6); + return; + } +} + +void __exit xfrm6_tunnel_fini(void) +{ + X6TPRINTK3(KERN_DEBUG "%s()\n", __FUNCTION__); + + xfrm6_tunnel_spi_fini(); + if (inet6_del_protocol(&xfrm6_tunnel_protocol, IPPROTO_IPV6) < 0) + X6TPRINTK1(KERN_ERR + "xfrm6_tunnel close: can't remove protocol\n"); + if (xfrm_unregister_type(&xfrm6_tunnel_type, AF_INET6) < 0) + X6TPRINTK1(KERN_ERR + "xfrm6_tunnel close: can't remove xfrm type\n"); +} -- cgit v1.2.3 From b55ee90963215277dc012ba65655ad5fb9fb03c4 Mon Sep 17 00:00:00 2001 From: Hideaki Yoshifuji Date: Sat, 19 Jun 2004 09:21:08 +0900 Subject: [XFRM] fix dependency issues for CONFIG_IPV6=m. --- include/net/xfrm.h | 2 +- net/ipv4/ah4.c | 5 ++++- net/ipv4/esp4.c | 13 +++++++++---- net/ipv4/ipcomp.c | 10 +++++++--- net/ipv6/ah6.c | 6 +++++- net/ipv6/esp6.c | 13 ++++++++----- net/ipv6/ipcomp6.c | 6 +++++- net/xfrm/Makefile | 2 +- net/xfrm/xfrm_export.c | 2 +- net/xfrm/xfrm_output.c | 46 ---------------------------------------------- net/xfrm/xfrm_state.c | 10 ++++++++++ 11 files changed, 51 insertions(+), 64 deletions(-) delete mode 100644 net/xfrm/xfrm_output.c (limited to 'include') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index d718cb222187..c36a3aed38c2 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -816,7 +816,7 @@ extern void xfrm_state_flush(u8 proto); extern int xfrm_replay_check(struct xfrm_state *x, u32 seq); extern void xfrm_replay_advance(struct xfrm_state *x, u32 seq); extern int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl); -extern int xfrm_check_output(struct xfrm_state *x, struct sk_buff *skb, unsigned short family); +extern int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb); extern int xfrm4_rcv(struct sk_buff *skb); extern int xfrm4_tunnel_register(struct xfrm_tunnel *handler); extern int xfrm4_tunnel_deregister(struct xfrm_tunnel *handler); diff --git a/net/ipv4/ah4.c b/net/ipv4/ah4.c index 0d62398f71a6..cbe0c7295c2d 100644 --- a/net/ipv4/ah4.c +++ b/net/ipv4/ah4.c @@ -74,12 +74,15 @@ static int ah_output(struct sk_buff **pskb) } spin_lock_bh(&x->lock); - err = xfrm_check_output(x, *pskb, AF_INET); + err = xfrm_state_check(x, *pskb); if (err) goto error; iph = (*pskb)->nh.iph; if (x->props.mode) { + err = xfrm4_tunnel_check_size(*pskb); + if (err) + goto error; top_iph = (struct iphdr*)skb_push(*pskb, x->props.header_len); top_iph->ihl = 5; top_iph->version = 4; diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 4755e0dcc90d..acecd602a2c8 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -49,19 +49,24 @@ int esp_output(struct sk_buff **pskb) } spin_lock_bh(&x->lock); - err = xfrm_check_output(x, *pskb, AF_INET); + err = xfrm_state_check(x, *pskb); if (err) goto error; - err = -ENOMEM; - /* Strip IP header in transport mode. Save it. */ - if (!x->props.mode) { + if (x->props.mode) { + err = xfrm4_tunnel_check_size(*pskb); + if (err) + goto error; + } else { + /* Strip IP header in transport mode. Save it. */ iph = (*pskb)->nh.iph; memcpy(&tmp_iph, iph, iph->ihl*4); __skb_pull(*pskb, iph->ihl*4); } /* Now skb is pure payload to encrypt */ + err = -ENOMEM; + /* Round to block size */ clen = (*pskb)->len; diff --git a/net/ipv4/ipcomp.c b/net/ipv4/ipcomp.c index e20c7e43b215..7201cacf4e5c 100644 --- a/net/ipv4/ipcomp.c +++ b/net/ipv4/ipcomp.c @@ -164,12 +164,16 @@ static int ipcomp_output(struct sk_buff **pskb) } spin_lock_bh(&x->lock); - err = xfrm_check_output(x, *pskb, AF_INET); + err = xfrm_state_check(x, *pskb); if (err) goto error; - /* Don't bother compressing */ - if (!x->props.mode) { + if (x->props.mode) { + err = xfrm4_tunnel_check_size(*pskb); + if (err) + goto error; + } else { + /* Don't bother compressing */ iph = (*pskb)->nh.iph; hdr_len = iph->ihl * 4; } diff --git a/net/ipv6/ah6.c b/net/ipv6/ah6.c index 185092c0d6b5..bb7ee1a25598 100644 --- a/net/ipv6/ah6.c +++ b/net/ipv6/ah6.c @@ -163,11 +163,15 @@ int ah6_output(struct sk_buff **pskb) } spin_lock_bh(&x->lock); - err = xfrm_check_output(x, *pskb, AF_INET6); + err = xfrm_state_check(x, *pskb); if (err) goto error; if (x->props.mode) { + err = xfrm6_tunnel_check_size(*pskb); + if (err) + goto error; + iph = (*pskb)->nh.ipv6h; (*pskb)->nh.ipv6h = (struct ipv6hdr*)skb_push(*pskb, x->props.header_len); (*pskb)->nh.ipv6h->version = 6; diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 8156942b141f..fb2720697d96 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -65,14 +65,16 @@ int esp6_output(struct sk_buff **pskb) } spin_lock_bh(&x->lock); - err = xfrm_check_output(x, *pskb, AF_INET6); + err = xfrm_state_check(x, *pskb); if (err) goto error; - err = -ENOMEM; - - /* Strip IP header in transport mode. Save it. */ - if (!x->props.mode) { + if (x->props.mode) { + err = xfrm6_tunnel_check_size(*pskb); + if (err) + goto error; + } else { + /* Strip IP header in transport mode. Save it. */ hdr_len = ip6_find_1stfragopt(*pskb, &prevhdr); nexthdr = *prevhdr; *prevhdr = IPPROTO_ESP; @@ -86,6 +88,7 @@ int esp6_output(struct sk_buff **pskb) } /* Now skb is pure payload to encrypt */ + err = -ENOMEM; /* Round to block size */ clen = (*pskb)->len; diff --git a/net/ipv6/ipcomp6.c b/net/ipv6/ipcomp6.c index 18d0f79dffa7..d129078f0b8f 100644 --- a/net/ipv6/ipcomp6.c +++ b/net/ipv6/ipcomp6.c @@ -140,11 +140,15 @@ static int ipcomp6_output(struct sk_buff **pskb) spin_lock_bh(&x->lock); - err = xfrm_check_output(x, *pskb, AF_INET6); + err = xfrm_state_check(x, *pskb); if (err) goto error; if (x->props.mode) { + err = xfrm6_tunnel_check_size(*pskb); + if (err) + goto error; + hdr_len = sizeof(struct ipv6hdr); nexthdr = IPPROTO_IPV6; iph = (*pskb)->nh.ipv6h; diff --git a/net/xfrm/Makefile b/net/xfrm/Makefile index 7cf00be13197..12bf0f88616c 100644 --- a/net/xfrm/Makefile +++ b/net/xfrm/Makefile @@ -2,7 +2,7 @@ # Makefile for the XFRM subsystem. # -obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_input.o xfrm_algo.o xfrm_output.o \ +obj-$(CONFIG_XFRM) := xfrm_policy.o xfrm_state.o xfrm_input.o xfrm_algo.o \ xfrm_export.o obj-$(CONFIG_XFRM_USER) += xfrm_user.o diff --git a/net/xfrm/xfrm_export.c b/net/xfrm/xfrm_export.c index fdd4d0ee7af7..e77179ccf742 100644 --- a/net/xfrm/xfrm_export.c +++ b/net/xfrm/xfrm_export.c @@ -18,6 +18,7 @@ EXPORT_SYMBOL(xfrm_state_add); EXPORT_SYMBOL(xfrm_state_update); EXPORT_SYMBOL(xfrm_state_check_expire); EXPORT_SYMBOL(xfrm_state_check_space); +EXPORT_SYMBOL(xfrm_state_check); EXPORT_SYMBOL(xfrm_state_lookup); EXPORT_SYMBOL(xfrm_state_register_afinfo); EXPORT_SYMBOL(xfrm_state_unregister_afinfo); @@ -27,7 +28,6 @@ EXPORT_SYMBOL(xfrm_state_delete_tunnel); EXPORT_SYMBOL(xfrm_replay_check); EXPORT_SYMBOL(xfrm_replay_advance); EXPORT_SYMBOL(xfrm_check_selectors); -EXPORT_SYMBOL(xfrm_check_output); EXPORT_SYMBOL(__secpath_destroy); EXPORT_SYMBOL(secpath_dup); EXPORT_SYMBOL(xfrm_get_acqseq); diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c deleted file mode 100644 index ae72dfde26d4..000000000000 --- a/net/xfrm/xfrm_output.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * generic xfrm output routines - * - * Copyright (c) 2003 James Morris - * - * 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 - -int xfrm_check_output(struct xfrm_state *x, - struct sk_buff *skb, unsigned short family) -{ - int err; - - err = xfrm_state_check_expire(x); - if (err) - goto out; - - if (x->props.mode) { - switch (family) { - case AF_INET: - err = xfrm4_tunnel_check_size(skb); - break; - - case AF_INET6: - err = xfrm6_tunnel_check_size(skb); - break; - - default: - err = -EINVAL; - } - - if (err) - goto out; - } - - err = xfrm_state_check_space(x, skb); -out: - return err; -} diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 44cb5240fa8f..160bb617d7eb 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -531,6 +531,16 @@ int xfrm_state_check_space(struct xfrm_state *x, struct sk_buff *skb) return 0; } +int xfrm_state_check(struct xfrm_state *x, struct sk_buff *skb) +{ + int err = xfrm_state_check_expire(x); + if (err < 0) + goto err; + err = xfrm_state_check_space(x, skb); +err: + return err; +} + struct xfrm_state * xfrm_state_lookup(xfrm_address_t *daddr, u32 spi, u8 proto, unsigned short family) -- cgit v1.2.3 From c3b2adbfeb9e3bbf898cf824df21ac822a95348a Mon Sep 17 00:00:00 2001 From: Hideaki Yoshifuji Date: Fri, 18 Jun 2004 21:55:24 -0700 Subject: [NET]: Fix some userland header bustage. --- include/linux/netfilter.h | 1 + include/linux/netfilter_arp/arp_tables.h | 2 +- include/linux/netfilter_ipv4/ip_tables.h | 3 ++- include/linux/netfilter_ipv6/ip6_tables.h | 3 +++ 4 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index b20c79258825..83f9668653ca 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -10,6 +10,7 @@ #include #include #endif +#include /* Responses from hook functions. */ #define NF_DROP 0 diff --git a/include/linux/netfilter_arp/arp_tables.h b/include/linux/netfilter_arp/arp_tables.h index 78c4f7142267..d6a7188b525c 100644 --- a/include/linux/netfilter_arp/arp_tables.h +++ b/include/linux/netfilter_arp/arp_tables.h @@ -16,7 +16,7 @@ #include #include #endif - +#include #include #define ARPT_FUNCTION_MAXNAMELEN 30 diff --git a/include/linux/netfilter_ipv4/ip_tables.h b/include/linux/netfilter_ipv4/ip_tables.h index f43f1dddebc4..dccc68052c88 100644 --- a/include/linux/netfilter_ipv4/ip_tables.h +++ b/include/linux/netfilter_ipv4/ip_tables.h @@ -22,6 +22,7 @@ #include #include #endif +#include #include #define IPT_FUNCTION_MAXNAMELEN 30 @@ -336,8 +337,8 @@ ipt_get_target(struct ipt_entry *e) /* * Main firewall chains definitions and global var's definitions. */ -static DECLARE_MUTEX(ipt_mutex); #ifdef __KERNEL__ +static DECLARE_MUTEX(ipt_mutex); #include extern void ipt_init(void) __init; diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h index a0995271934a..f9983d16cc1c 100644 --- a/include/linux/netfilter_ipv6/ip6_tables.h +++ b/include/linux/netfilter_ipv6/ip6_tables.h @@ -22,6 +22,7 @@ #include #include #endif +#include #include #define IP6T_FUNCTION_MAXNAMELEN 30 @@ -106,7 +107,9 @@ struct ip6t_counters u_int64_t pcnt, bcnt; /* Packet and byte counters */ }; +#ifdef __KERNEL__ static DECLARE_MUTEX(ip6t_mutex); +#endif /* Values for "flag" field in struct ip6t_ip6 (general ip6 structure). */ #define IP6T_F_PROTO 0x01 /* Set if rule cares about upper -- cgit v1.2.3 From 56a6f4d0679764b124298f80f113b67db22e6ff4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sat, 19 Jun 2004 21:03:06 -0300 Subject: [NET] generalise tcp_add_data, skb_split and tcp_copy_to_page Signed-off-by: Arnaldo Carvalho de Melo --- include/linux/errqueue.h | 4 ++ include/linux/skbuff.h | 24 +++++++ include/net/checksum.h | 70 +-------------------- include/net/ip.h | 2 +- include/net/ip6_checksum.h | 94 ++++++++++++++++++++++++++++ include/net/sock.h | 24 +++++++ include/net/tcp.h | 1 + net/core/skbuff.c | 76 ++++++++++++++++++++++ net/ipv4/netfilter/ip_conntrack_core.c | 1 + net/ipv4/netfilter/ip_conntrack_standalone.c | 1 + net/ipv4/netfilter/ip_fw_compat_redir.c | 1 + net/ipv4/netfilter/ip_nat_standalone.c | 1 + net/ipv4/netfilter/ipt_MASQUERADE.c | 1 + net/ipv4/tcp.c | 49 +-------------- net/ipv4/tcp_output.c | 64 ------------------- net/ipv6/icmp.c | 2 +- net/ipv6/mcast.c | 2 +- net/ipv6/ndisc.c | 2 +- net/ipv6/raw.c | 2 + net/ipv6/tcp_ipv6.c | 1 + net/ipv6/udp.c | 2 +- 21 files changed, 238 insertions(+), 186 deletions(-) create mode 100644 include/net/ip6_checksum.h (limited to 'include') diff --git a/include/linux/errqueue.h b/include/linux/errqueue.h index 0d87e62ec9e7..174582fedb8b 100644 --- a/include/linux/errqueue.h +++ b/include/linux/errqueue.h @@ -22,6 +22,10 @@ struct sock_extended_err #ifdef __KERNEL__ #include +#include +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) +#include +#endif #define SKB_EXT_ERR(skb) ((struct sock_exterr_skb *) ((skb)->cb)) diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 7bf6501a9024..f777878608ab 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -27,6 +27,7 @@ #include #include #include +#include #define HAVE_ALLOC_SKB /* For the drivers to know */ #define HAVE_ALIGNABLE_SKB /* Ditto 8) */ @@ -971,6 +972,27 @@ static inline struct sk_buff *skb_padto(struct sk_buff *skb, unsigned int len) return skb_pad(skb, len-size); } +static inline int skb_add_data(struct sk_buff *skb, + char __user *from, int copy) +{ + const int off = skb->len; + + if (skb->ip_summed == CHECKSUM_NONE) { + int err = 0; + unsigned int csum = csum_and_copy_from_user(from, + skb_put(skb, copy), + copy, 0, &err); + if (!err) { + skb->csum = csum_block_add(skb->csum, csum, off); + return 0; + } + } else if (!copy_from_user(skb_put(skb, copy), from, copy)) + return 0; + + __skb_trim(skb, off); + return -EFAULT; +} + /** * skb_linearize - convert paged skb to linear one * @skb: buffer to linarize @@ -1034,6 +1056,8 @@ extern unsigned int skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, int len, unsigned int csum); extern void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); +extern void skb_split(struct sk_buff *skb, + struct sk_buff *skb1, const u32 len); extern void skb_init(void); extern void skb_add_mtu(int mtu); diff --git a/include/net/checksum.h b/include/net/checksum.h index cd3c52a594e4..43f40235114e 100644 --- a/include/net/checksum.h +++ b/include/net/checksum.h @@ -16,83 +16,15 @@ * 2 of the License, or (at your option) any later version. */ -/* - * Fixes: - * - * Ralf Baechle : generic ipv6 checksum - * - */ - #ifndef _CHECKSUM_H #define _CHECKSUM_H +#include #include #include -#include -#include #include #include -#ifndef _HAVE_ARCH_IPV6_CSUM - -static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr, - struct in6_addr *daddr, - __u16 len, - unsigned short proto, - unsigned int csum) -{ - - int carry; - __u32 ulen; - __u32 uproto; - - csum += saddr->s6_addr32[0]; - carry = (csum < saddr->s6_addr32[0]); - csum += carry; - - csum += saddr->s6_addr32[1]; - carry = (csum < saddr->s6_addr32[1]); - csum += carry; - - csum += saddr->s6_addr32[2]; - carry = (csum < saddr->s6_addr32[2]); - csum += carry; - - csum += saddr->s6_addr32[3]; - carry = (csum < saddr->s6_addr32[3]); - csum += carry; - - csum += daddr->s6_addr32[0]; - carry = (csum < daddr->s6_addr32[0]); - csum += carry; - - csum += daddr->s6_addr32[1]; - carry = (csum < daddr->s6_addr32[1]); - csum += carry; - - csum += daddr->s6_addr32[2]; - carry = (csum < daddr->s6_addr32[2]); - csum += carry; - - csum += daddr->s6_addr32[3]; - carry = (csum < daddr->s6_addr32[3]); - csum += carry; - - ulen = htonl((__u32) len); - csum += ulen; - carry = (csum < ulen); - csum += carry; - - uproto = htonl(proto); - csum += uproto; - carry = (csum < uproto); - csum += carry; - - return csum_fold(csum); -} - -#endif - #ifndef _HAVE_ARCH_COPY_AND_CSUM_FROM_USER static inline unsigned int csum_and_copy_from_user (const char __user *src, char *dst, diff --git a/include/net/ip.h b/include/net/ip.h index 5a683ccd4cb0..d36a3b230819 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -37,7 +37,7 @@ #include #endif -#include /* struct sock */ +struct sock; struct inet_skb_parm { diff --git a/include/net/ip6_checksum.h b/include/net/ip6_checksum.h new file mode 100644 index 000000000000..3dfc885bdf25 --- /dev/null +++ b/include/net/ip6_checksum.h @@ -0,0 +1,94 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Checksumming functions for IPv6 + * + * Authors: Jorge Cwik, + * Arnt Gulbrandsen, + * Borrows very liberally from tcp.c and ip.c, see those + * files for more names. + * + * 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. + */ + +/* + * Fixes: + * + * Ralf Baechle : generic ipv6 checksum + * + */ + +#ifndef _CHECKSUM_IPV6_H +#define _CHECKSUM_IPV6_H + +#include +#include +#include +#include +#include + +#ifndef _HAVE_ARCH_IPV6_CSUM + +static __inline__ unsigned short int csum_ipv6_magic(struct in6_addr *saddr, + struct in6_addr *daddr, + __u16 len, + unsigned short proto, + unsigned int csum) +{ + + int carry; + __u32 ulen; + __u32 uproto; + + csum += saddr->s6_addr32[0]; + carry = (csum < saddr->s6_addr32[0]); + csum += carry; + + csum += saddr->s6_addr32[1]; + carry = (csum < saddr->s6_addr32[1]); + csum += carry; + + csum += saddr->s6_addr32[2]; + carry = (csum < saddr->s6_addr32[2]); + csum += carry; + + csum += saddr->s6_addr32[3]; + carry = (csum < saddr->s6_addr32[3]); + csum += carry; + + csum += daddr->s6_addr32[0]; + carry = (csum < daddr->s6_addr32[0]); + csum += carry; + + csum += daddr->s6_addr32[1]; + carry = (csum < daddr->s6_addr32[1]); + csum += carry; + + csum += daddr->s6_addr32[2]; + carry = (csum < daddr->s6_addr32[2]); + csum += carry; + + csum += daddr->s6_addr32[3]; + carry = (csum < daddr->s6_addr32[3]); + csum += carry; + + ulen = htonl((__u32) len); + csum += ulen; + carry = (csum < ulen); + csum += carry; + + uproto = htonl(proto); + csum += uproto; + carry = (csum < uproto); + csum += carry; + + return csum_fold(csum); +} + +#endif +#endif diff --git a/include/net/sock.h b/include/net/sock.h index 38b90c8ab25f..5624b084742f 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -53,6 +53,7 @@ #include #include +#include /* * This structure really needs to be cleaned up. @@ -923,6 +924,29 @@ static inline void sk_charge_skb(struct sock *sk, struct sk_buff *skb) sk->sk_forward_alloc -= skb->truesize; } +static inline int skb_copy_to_page(struct sock *sk, char __user *from, + struct sk_buff *skb, struct page *page, + int off, int copy) +{ + if (skb->ip_summed == CHECKSUM_NONE) { + int err = 0; + unsigned int csum = csum_and_copy_from_user(from, + page_address(page) + off, + copy, 0, &err); + if (err) + return err; + skb->csum = csum_block_add(skb->csum, csum, skb->len); + } else if (copy_from_user(page_address(page) + off, from, copy)) + return -EFAULT; + + skb->len += copy; + skb->data_len += copy; + skb->truesize += copy; + sk->sk_wmem_queued += copy; + sk->sk_forward_alloc -= copy; + return 0; +} + /* * Queue a received datagram if it will fit. Stream and sequenced * protocols can't normally use this as they need to fit buffers in diff --git a/include/net/tcp.h b/include/net/tcp.h index 52f27edef69e..3a323cd1e79f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -33,6 +33,7 @@ #include #include #include +#include #if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) #include #endif diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 7d72cdb49a57..f191cae7462e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1263,6 +1263,81 @@ void skb_add_mtu(int mtu) } #endif +static void inline skb_split_inside_header(struct sk_buff *skb, + struct sk_buff* skb1, + const u32 len, const int pos) +{ + int i; + + memcpy(skb_put(skb1, pos - len), skb->data + len, pos - len); + + /* And move data appendix as is. */ + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) + skb_shinfo(skb1)->frags[i] = skb_shinfo(skb)->frags[i]; + + skb_shinfo(skb1)->nr_frags = skb_shinfo(skb)->nr_frags; + skb_shinfo(skb)->nr_frags = 0; + skb1->data_len = skb->data_len; + skb1->len += skb1->data_len; + skb->data_len = 0; + skb->len = len; + skb->tail = skb->data + len; +} + +static void inline skb_split_no_header(struct sk_buff *skb, + struct sk_buff* skb1, + const u32 len, int pos) +{ + int i, k = 0; + const int nfrags = skb_shinfo(skb)->nr_frags; + + skb_shinfo(skb)->nr_frags = 0; + skb1->len = skb1->data_len = skb->len - len; + skb->len = len; + skb->data_len = len - pos; + + for (i = 0; i < nfrags; i++) { + int size = skb_shinfo(skb)->frags[i].size; + + if (pos + size > len) { + skb_shinfo(skb1)->frags[k] = skb_shinfo(skb)->frags[i]; + + if (pos < len) { + /* Split frag. + * We have to variants in this case: + * 1. Move all the frag to the second + * part, if it is possible. F.e. + * this approach is mandatory for TUX, + * where splitting is expensive. + * 2. Split is accurately. We make this. + */ + get_page(skb_shinfo(skb)->frags[i].page); + skb_shinfo(skb1)->frags[0].page_offset += len - pos; + skb_shinfo(skb1)->frags[0].size -= len - pos; + skb_shinfo(skb)->frags[i].size = len - pos; + skb_shinfo(skb)->nr_frags++; + } + k++; + } else + skb_shinfo(skb)->nr_frags++; + pos += size; + } + skb_shinfo(skb1)->nr_frags = k; +} + +/** + * skb_split - Split fragmented skb to two parts at length len. + */ +void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len) +{ + int pos = skb_headlen(skb); + + if (len < pos) /* Split line is inside header. */ + skb_split_inside_header(skb, skb1, len, pos); + else /* Second chunk has no header, nothing to copy. */ + skb_split_no_header(skb, skb1, len, pos); +} + void __init skb_init(void) { skbuff_head_cache = kmem_cache_create("skbuff_head_cache", @@ -1300,3 +1375,4 @@ EXPORT_SYMBOL(skb_queue_head); EXPORT_SYMBOL(skb_queue_tail); EXPORT_SYMBOL(skb_unlink); EXPORT_SYMBOL(skb_append); +EXPORT_SYMBOL(skb_split); diff --git a/net/ipv4/netfilter/ip_conntrack_core.c b/net/ipv4/netfilter/ip_conntrack_core.c index 50a467898671..47686afd71a6 100644 --- a/net/ipv4/netfilter/ip_conntrack_core.c +++ b/net/ipv4/netfilter/ip_conntrack_core.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include diff --git a/net/ipv4/netfilter/ip_conntrack_standalone.c b/net/ipv4/netfilter/ip_conntrack_standalone.c index 4d378394aac4..fbe26d1ca0ad 100644 --- a/net/ipv4/netfilter/ip_conntrack_standalone.c +++ b/net/ipv4/netfilter/ip_conntrack_standalone.c @@ -24,6 +24,7 @@ #include #endif #include +#include #define ASSERT_READ_LOCK(x) MUST_BE_READ_LOCKED(&ip_conntrack_lock) #define ASSERT_WRITE_LOCK(x) MUST_BE_WRITE_LOCKED(&ip_conntrack_lock) diff --git a/net/ipv4/netfilter/ip_fw_compat_redir.c b/net/ipv4/netfilter/ip_fw_compat_redir.c index 6a24a5cb17e0..7f68c1ed5a87 100644 --- a/net/ipv4/netfilter/ip_fw_compat_redir.c +++ b/net/ipv4/netfilter/ip_fw_compat_redir.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c index a84453f899fe..9765fd2d5cf1 100644 --- a/net/ipv4/netfilter/ip_nat_standalone.c +++ b/net/ipv4/netfilter/ip_nat_standalone.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include diff --git a/net/ipv4/netfilter/ipt_MASQUERADE.c b/net/ipv4/netfilter/ipt_MASQUERADE.c index fb499905f05d..54bc4684cc9d 100644 --- a/net/ipv4/netfilter/ipt_MASQUERADE.c +++ b/net/ipv4/netfilter/ipt_MASQUERADE.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index d43b43938972..0b29f833d29b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -948,53 +948,6 @@ ssize_t tcp_sendpage(struct socket *sock, struct page *page, int offset, #define TCP_PAGE(sk) (inet_sk(sk)->sndmsg_page) #define TCP_OFF(sk) (inet_sk(sk)->sndmsg_off) -static inline int tcp_copy_to_page(struct sock *sk, char __user *from, - struct sk_buff *skb, struct page *page, - int off, int copy) -{ - int err = 0; - unsigned int csum; - - if (skb->ip_summed == CHECKSUM_NONE) { - csum = csum_and_copy_from_user(from, page_address(page) + off, - copy, 0, &err); - if (err) return err; - skb->csum = csum_block_add(skb->csum, csum, skb->len); - } else { - if (copy_from_user(page_address(page) + off, from, copy)) - return -EFAULT; - } - - skb->len += copy; - skb->data_len += copy; - skb->truesize += copy; - sk->sk_wmem_queued += copy; - sk->sk_forward_alloc -= copy; - return 0; -} - -static inline int skb_add_data(struct sk_buff *skb, char __user *from, int copy) -{ - int err = 0; - unsigned int csum; - int off = skb->len; - - if (skb->ip_summed == CHECKSUM_NONE) { - csum = csum_and_copy_from_user(from, skb_put(skb, copy), - copy, 0, &err); - if (!err) { - skb->csum = csum_block_add(skb->csum, csum, off); - return 0; - } - } else { - if (!copy_from_user(skb_put(skb, copy), from, copy)) - return 0; - } - - __skb_trim(skb, off); - return -EFAULT; -} - static inline int select_size(struct sock *sk, struct tcp_opt *tp) { int tmp = tp->mss_cache_std; @@ -1138,7 +1091,7 @@ new_segment: /* Time to copy data. We are close to * the end! */ - err = tcp_copy_to_page(sk, from, skb, page, + err = skb_copy_to_page(sk, from, skb, page, off, copy); if (err) { /* If this page was new, give it to the diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index ac6c55259e1a..ad0c2ac89a1a 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -354,70 +354,6 @@ void tcp_push_one(struct sock *sk, unsigned cur_mss) } } -/* Split fragmented skb to two parts at length len. */ - -static void skb_split(struct sk_buff *skb, struct sk_buff *skb1, u32 len) -{ - int i; - int pos = skb_headlen(skb); - - if (len < pos) { - /* Split line is inside header. */ - memcpy(skb_put(skb1, pos-len), skb->data + len, pos-len); - - /* And move data appendix as is. */ - for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) - skb_shinfo(skb1)->frags[i] = skb_shinfo(skb)->frags[i]; - - skb_shinfo(skb1)->nr_frags = skb_shinfo(skb)->nr_frags; - skb_shinfo(skb)->nr_frags = 0; - - skb1->data_len = skb->data_len; - skb1->len += skb1->data_len; - skb->data_len = 0; - skb->len = len; - skb->tail = skb->data+len; - } else { - int k = 0; - int nfrags = skb_shinfo(skb)->nr_frags; - - /* Second chunk has no header, nothing to copy. */ - - skb_shinfo(skb)->nr_frags = 0; - skb1->len = skb1->data_len = skb->len - len; - skb->len = len; - skb->data_len = len - pos; - - for (i=0; ifrags[i].size; - if (pos + size > len) { - skb_shinfo(skb1)->frags[k] = skb_shinfo(skb)->frags[i]; - - if (pos < len) { - /* Split frag. - * We have to variants in this case: - * 1. Move all the frag to the second - * part, if it is possible. F.e. - * this approach is mandatory for TUX, - * where splitting is expensive. - * 2. Split is accurately. We make this. - */ - get_page(skb_shinfo(skb)->frags[i].page); - skb_shinfo(skb1)->frags[0].page_offset += (len-pos); - skb_shinfo(skb1)->frags[0].size -= (len-pos); - skb_shinfo(skb)->frags[i].size = len-pos; - skb_shinfo(skb)->nr_frags++; - } - k++; - } else { - skb_shinfo(skb)->nr_frags++; - } - pos += size; - } - skb_shinfo(skb1)->nr_frags = k; - } -} - /* Function to create two new TCP segments. Shrinks the given segment * to the specified size and appends a new segment with the rest of the * packet to the list. This won't be called frequently, I hope. diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index cfecd15d9b5c..6bc7ebcc32fd 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -55,7 +55,7 @@ #include #include -#include +#include #include #include #include diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index b351145770d2..0bd071691ba4 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -60,7 +60,7 @@ #include #include -#include +#include /* Set to 3 to get tracing... */ #define MCAST_DEBUG 2 diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index aa6b74d9b08a..6330513fffaf 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -77,7 +77,7 @@ #include #include -#include +#include #include #include diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 3d2961c48955..ebd24bb8f7e2 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include #include #include diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index f410d8c40d78..c41ddd55f958 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 63ef59317f78..3ddbfa88ca2a 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -51,7 +51,7 @@ #include #include -#include +#include #include #include -- cgit v1.2.3 From a4f482782cbd23445af05439f92f165761f29a50 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sat, 19 Jun 2004 20:37:51 -0700 Subject: [PATCH] m68k: handle new gcc's M68k: Fixes for when compiling with modern gcc (from Roman Zippel): - Avoid warning 'use of memory input without lvalue in asm operand 0 is deprecated' of newer gcc - Replace some '%/' with offical '%%' to escape a '%' Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Torvalds --- arch/m68k/kernel/signal.c | 34 +++++++++++++++++----------------- include/asm-m68k/ucontext.h | 6 ++---- 2 files changed, 19 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index 0fd62ab0f21c..095383287e01 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c @@ -228,8 +228,8 @@ static inline int restore_fpu_state(struct sigcontext *sc) goto out; __asm__ volatile (".chip 68k/68881\n\t" - "fmovemx %0,%/fp0-%/fp1\n\t" - "fmoveml %1,%/fpcr/%/fpsr/%/fpiar\n\t" + "fmovemx %0,%%fp0-%%fp1\n\t" + "fmoveml %1,%%fpcr/%%fpsr/%%fpiar\n\t" ".chip 68k" : /* no outputs */ : "m" (*sc->sc_fpregs), "m" (*sc->sc_fpcntl)); @@ -258,7 +258,7 @@ static inline int rt_restore_fpu_state(struct ucontext *uc) if (FPU_IS_EMU) { /* restore fpu control register */ if (__copy_from_user(current->thread.fpcntl, - &uc->uc_mcontext.fpregs.f_pcr, 12)) + uc->uc_mcontext.fpregs.f_fpcntl, 12)) goto out; /* restore all other fpu register */ if (__copy_from_user(current->thread.fp, @@ -298,12 +298,12 @@ static inline int rt_restore_fpu_state(struct ucontext *uc) sizeof(fpregs))) goto out; __asm__ volatile (".chip 68k/68881\n\t" - "fmovemx %0,%/fp0-%/fp7\n\t" - "fmoveml %1,%/fpcr/%/fpsr/%/fpiar\n\t" + "fmovemx %0,%%fp0-%%fp7\n\t" + "fmoveml %1,%%fpcr/%%fpsr/%%fpiar\n\t" ".chip 68k" : /* no outputs */ : "m" (*fpregs.f_fpregs), - "m" (fpregs.f_pcr)); + "m" (*fpregs.f_fpcntl)); } if (context_size && __copy_from_user(fpstate + 4, (long *)&uc->uc_fpstate + 1, @@ -586,12 +586,12 @@ static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs) sc->sc_fpstate[0x38] |= 1 << 3; } __asm__ volatile (".chip 68k/68881\n\t" - "fmovemx %/fp0-%/fp1,%0\n\t" - "fmoveml %/fpcr/%/fpsr/%/fpiar,%1\n\t" + "fmovemx %%fp0-%%fp1,%0\n\t" + "fmoveml %%fpcr/%%fpsr/%%fpiar,%1\n\t" ".chip 68k" - : /* no outputs */ - : "m" (*sc->sc_fpregs), - "m" (*sc->sc_fpcntl) + : "=m" (*sc->sc_fpregs), + "=m" (*sc->sc_fpcntl) + : /* no inputs */ : "memory"); } } @@ -604,7 +604,7 @@ static inline int rt_save_fpu_state(struct ucontext *uc, struct pt_regs *regs) if (FPU_IS_EMU) { /* save fpu control register */ - err |= copy_to_user(&uc->uc_mcontext.fpregs.f_pcr, + err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpcntl, current->thread.fpcntl, 12); /* save all other fpu register */ err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpregs, @@ -631,12 +631,12 @@ static inline int rt_save_fpu_state(struct ucontext *uc, struct pt_regs *regs) fpstate[0x38] |= 1 << 3; } __asm__ volatile (".chip 68k/68881\n\t" - "fmovemx %/fp0-%/fp7,%0\n\t" - "fmoveml %/fpcr/%/fpsr/%/fpiar,%1\n\t" + "fmovemx %%fp0-%%fp7,%0\n\t" + "fmoveml %%fpcr/%%fpsr/%%fpiar,%1\n\t" ".chip 68k" - : /* no outputs */ - : "m" (*fpregs.f_fpregs), - "m" (fpregs.f_pcr) + : "=m" (*fpregs.f_fpregs), + "=m" (*fpregs.f_fpcntl) + : /* no inputs */ : "memory"); err |= copy_to_user(&uc->uc_mcontext.fpregs, &fpregs, sizeof(fpregs)); diff --git a/include/asm-m68k/ucontext.h b/include/asm-m68k/ucontext.h index fb6d732ad7b2..e4e22669edc0 100644 --- a/include/asm-m68k/ucontext.h +++ b/include/asm-m68k/ucontext.h @@ -6,10 +6,8 @@ typedef int greg_t; typedef greg_t gregset_t[NGREG]; typedef struct fpregset { - int f_pcr; - int f_psr; - int f_fpiaddr; - int f_fpregs[8][3]; + int f_fpcntl[3]; + int f_fpregs[8*3]; } fpregset_t; struct mcontext { -- cgit v1.2.3 From bd992e78e07a8b76ff2e6f2d2f76c17a5d624273 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sat, 19 Jun 2004 20:38:02 -0700 Subject: [PATCH] m68k: new gcc optimizations M68k compiler updates from Roman Zippel: - Fix various lvalue warnings from newer gcc - Remove unnecessary volatile declarations - Change some constraints from "a" to "m" to generate slightly better code - Use "o" constraint for bitfield instructions - Use generic bitmap functions for some of the ext2/minix bitmap functions Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Torvalds --- include/asm-m68k/atomic.h | 28 +++++---- include/asm-m68k/bitops.h | 157 ++++++++++++++++------------------------------ include/asm-m68k/page.h | 18 +++--- include/asm-m68k/string.h | 17 +++-- 4 files changed, 86 insertions(+), 134 deletions(-) (limited to 'include') diff --git a/include/asm-m68k/atomic.h b/include/asm-m68k/atomic.h index d7ef37661179..6909d9694c67 100644 --- a/include/asm-m68k/atomic.h +++ b/include/asm-m68k/atomic.h @@ -18,36 +18,40 @@ typedef struct { int counter; } atomic_t; static __inline__ void atomic_add(int i, atomic_t *v) { - __asm__ __volatile__("addl %1,%0" : "=m" (*v) : "id" (i), "0" (*v)); + __asm__ __volatile__("addl %1,%0" : "+m" (*v) : "id" (i)); } static __inline__ void atomic_sub(int i, atomic_t *v) { - __asm__ __volatile__("subl %1,%0" : "=m" (*v) : "id" (i), "0" (*v)); + __asm__ __volatile__("subl %1,%0" : "+m" (*v) : "id" (i)); } -static __inline__ void atomic_inc(volatile atomic_t *v) +static __inline__ void atomic_inc(atomic_t *v) { - __asm__ __volatile__("addql #1,%0" : "=m" (*v): "0" (*v)); + __asm__ __volatile__("addql #1,%0" : "+m" (*v)); } -static __inline__ void atomic_dec(volatile atomic_t *v) +static __inline__ void atomic_dec(atomic_t *v) { - __asm__ __volatile__("subql #1,%0" : "=m" (*v): "0" (*v)); + __asm__ __volatile__("subql #1,%0" : "+m" (*v)); } -static __inline__ int atomic_dec_and_test(volatile atomic_t *v) +static __inline__ int atomic_dec_and_test(atomic_t *v) { char c; - __asm__ __volatile__("subql #1,%1; seq %0" : "=d" (c), "=m" (*v): "1" (*v)); + __asm__ __volatile__("subql #1,%1; seq %0" : "=d" (c), "+m" (*v)); return c != 0; } -#define atomic_clear_mask(mask, v) \ - __asm__ __volatile__("andl %1,%0" : "=m" (*v) : "id" (~(mask)),"0"(*v)) +static __inline__ void atomic_clear_mask(unsigned long mask, unsigned long *v) +{ + __asm__ __volatile__("andl %1,%0" : "+m" (*v) : "id" (~(mask))); +} -#define atomic_set_mask(mask, v) \ - __asm__ __volatile__("orl %1,%0" : "=m" (*v) : "id" (mask),"0"(*v)) +static __inline__ void atomic_set_mask(unsigned long mask, unsigned long *v) +{ + __asm__ __volatile__("orl %1,%0" : "+m" (*v) : "id" (mask)); +} /* Atomic operations are already serializing */ #define smp_mb__before_atomic_dec() barrier() diff --git a/include/asm-m68k/bitops.h b/include/asm-m68k/bitops.h index ec1e32ecb914..05ccc865d77b 100644 --- a/include/asm-m68k/bitops.h +++ b/include/asm-m68k/bitops.h @@ -23,25 +23,24 @@ #define __test_and_set_bit(nr,vaddr) test_and_set_bit(nr,vaddr) -static inline int __constant_test_and_set_bit(int nr, - volatile unsigned long *vaddr) +static inline int __constant_test_and_set_bit(int nr, unsigned long *vaddr) { + char *p = (char *)vaddr + (nr ^ 31) / 8; char retval; __asm__ __volatile__ ("bset %2,%1; sne %0" - : "=d" (retval), "+m" (((volatile char *)vaddr)[(nr^31) >> 3]) - : "di" (nr & 7)); + : "=d" (retval), "+m" (*p) + : "di" (nr & 7)); return retval; } -static inline int __generic_test_and_set_bit(int nr, - volatile unsigned long *vaddr) +static inline int __generic_test_and_set_bit(int nr, unsigned long *vaddr) { char retval; - __asm__ __volatile__ ("bfset %2@{%1:#1}; sne %0" - : "=d" (retval) : "d" (nr^31), "a" (vaddr) : "memory"); + __asm__ __volatile__ ("bfset %2{%1:#1}; sne %0" + : "=d" (retval) : "d" (nr^31), "o" (*vaddr) : "memory"); return retval; } @@ -53,16 +52,17 @@ static inline int __generic_test_and_set_bit(int nr, #define __set_bit(nr,vaddr) set_bit(nr,vaddr) -static inline void __constant_set_bit(int nr, volatile unsigned long *vaddr) +static inline void __constant_set_bit(int nr, unsigned long *vaddr) { + char *p = (char *)vaddr + (nr ^ 31) / 8; __asm__ __volatile__ ("bset %1,%0" - : "+m" (((volatile char *)vaddr)[(nr^31) >> 3]) : "di" (nr & 7)); + : "+m" (*p) : "di" (nr & 7)); } -static inline void __generic_set_bit(int nr, volatile unsigned long *vaddr) +static inline void __generic_set_bit(int nr, unsigned long *vaddr) { - __asm__ __volatile__ ("bfset %1@{%0:#1}" - : : "d" (nr^31), "a" (vaddr) : "memory"); + __asm__ __volatile__ ("bfset %1{%0:#1}" + : : "d" (nr^31), "o" (*vaddr) : "memory"); } #define test_and_clear_bit(nr,vaddr) \ @@ -72,25 +72,24 @@ static inline void __generic_set_bit(int nr, volatile unsigned long *vaddr) #define __test_and_clear_bit(nr,vaddr) test_and_clear_bit(nr,vaddr) -static inline int __constant_test_and_clear_bit(int nr, - volatile unsigned long *vaddr) +static inline int __constant_test_and_clear_bit(int nr, unsigned long *vaddr) { + char *p = (char *)vaddr + (nr ^ 31) / 8; char retval; __asm__ __volatile__ ("bclr %2,%1; sne %0" - : "=d" (retval), "+m" (((volatile char *)vaddr)[(nr^31) >> 3]) - : "di" (nr & 7)); + : "=d" (retval), "+m" (*p) + : "di" (nr & 7)); return retval; } -static inline int __generic_test_and_clear_bit(int nr, - volatile unsigned long *vaddr) +static inline int __generic_test_and_clear_bit(int nr, unsigned long *vaddr) { char retval; - __asm__ __volatile__ ("bfclr %2@{%1:#1}; sne %0" - : "=d" (retval) : "d" (nr^31), "a" (vaddr) : "memory"); + __asm__ __volatile__ ("bfclr %2{%1:#1}; sne %0" + : "=d" (retval) : "d" (nr^31), "o" (*vaddr) : "memory"); return retval; } @@ -107,16 +106,17 @@ static inline int __generic_test_and_clear_bit(int nr, __generic_clear_bit(nr, vaddr)) #define __clear_bit(nr,vaddr) clear_bit(nr,vaddr) -static inline void __constant_clear_bit(int nr, volatile unsigned long *vaddr) +static inline void __constant_clear_bit(int nr, unsigned long *vaddr) { + char *p = (char *)vaddr + (nr ^ 31) / 8; __asm__ __volatile__ ("bclr %1,%0" - : "+m" (((volatile char *)vaddr)[(nr^31) >> 3]) : "di" (nr & 7)); + : "+m" (*p) : "di" (nr & 7)); } -static inline void __generic_clear_bit(int nr, volatile unsigned long *vaddr) +static inline void __generic_clear_bit(int nr, unsigned long *vaddr) { - __asm__ __volatile__ ("bfclr %1@{%0:#1}" - : : "d" (nr^31), "a" (vaddr) : "memory"); + __asm__ __volatile__ ("bfclr %1{%0:#1}" + : : "d" (nr^31), "o" (*vaddr) : "memory"); } #define test_and_change_bit(nr,vaddr) \ @@ -127,25 +127,24 @@ static inline void __generic_clear_bit(int nr, volatile unsigned long *vaddr) #define __test_and_change_bit(nr,vaddr) test_and_change_bit(nr,vaddr) #define __change_bit(nr,vaddr) change_bit(nr,vaddr) -static inline int __constant_test_and_change_bit(int nr, - volatile unsigned long *vaddr) +static inline int __constant_test_and_change_bit(int nr, unsigned long *vaddr) { + char *p = (char *)vaddr + (nr ^ 31) / 8; char retval; __asm__ __volatile__ ("bchg %2,%1; sne %0" - : "=d" (retval), "+m" (((volatile char *)vaddr)[(nr^31) >> 3]) - : "di" (nr & 7)); + : "=d" (retval), "+m" (*p) + : "di" (nr & 7)); return retval; } -static inline int __generic_test_and_change_bit(int nr, - volatile unsigned long *vaddr) +static inline int __generic_test_and_change_bit(int nr, unsigned long *vaddr) { char retval; - __asm__ __volatile__ ("bfchg %2@{%1:#1}; sne %0" - : "=d" (retval) : "d" (nr^31), "a" (vaddr) : "memory"); + __asm__ __volatile__ ("bfchg %2{%1:#1}; sne %0" + : "=d" (retval) : "d" (nr^31), "o" (*vaddr) : "memory"); return retval; } @@ -155,21 +154,22 @@ static inline int __generic_test_and_change_bit(int nr, __constant_change_bit(nr, vaddr) : \ __generic_change_bit(nr, vaddr)) -static inline void __constant_change_bit(int nr, volatile unsigned long *vaddr) +static inline void __constant_change_bit(int nr, unsigned long *vaddr) { + char *p = (char *)vaddr + (nr ^ 31) / 8; __asm__ __volatile__ ("bchg %1,%0" - : "+m" (((volatile char *)vaddr)[(nr^31) >> 3]) : "di" (nr & 7)); + : "+m" (*p) : "di" (nr & 7)); } -static inline void __generic_change_bit(int nr, volatile unsigned long *vaddr) +static inline void __generic_change_bit(int nr, unsigned long *vaddr) { - __asm__ __volatile__ ("bfchg %1@{%0:#1}" - : : "d" (nr^31), "a" (vaddr) : "memory"); + __asm__ __volatile__ ("bfchg %1{%0:#1}" + : : "d" (nr^31), "o" (*vaddr) : "memory"); } -static inline int test_bit(int nr, const volatile unsigned long *vaddr) +static inline int test_bit(int nr, const unsigned long *vaddr) { - return ((1UL << (nr & 31)) & (((const volatile unsigned long *) vaddr)[nr >> 5])) != 0; + return (vaddr[nr >> 5] & (1UL << (nr & 31))) != 0; } static inline int find_first_zero_bit(const unsigned long *vaddr, @@ -364,76 +364,27 @@ static inline int minix_find_first_zero_bit(const void *vaddr, unsigned size) return ((p - addr) << 4) + (res ^ 31); } -static inline int minix_test_and_set_bit(int nr, volatile void *vaddr) -{ - char retval; - - __asm__ __volatile__ ("bfset %2{%1:#1}; sne %0" - : "=d" (retval) : "d" (nr^15), "m" (*(volatile char *)vaddr) : "memory"); - - return retval; -} - -#define minix_set_bit(nr,addr) ((void)minix_test_and_set_bit(nr,addr)) +#define minix_test_and_set_bit(nr, addr) test_and_set_bit((nr) ^ 16, (unsigned long *)(addr)) +#define minix_set_bit(nr,addr) set_bit((nr) ^ 16, (unsigned long *)(addr)) +#define minix_test_and_clear_bit(nr, addr) test_and_clear_bit((nr) ^ 16, (unsigned long *)(addr)) -static inline int minix_test_and_clear_bit(int nr, volatile void *vaddr) +static inline int minix_test_bit(int nr, const void *vaddr) { - char retval; - - __asm__ __volatile__ ("bfclr %2{%1:#1}; sne %0" - : "=d" (retval) : "d" (nr^15), "m" (*(volatile char *) vaddr) : "memory"); - - return retval; -} - -static inline int minix_test_bit(int nr, const volatile void *vaddr) -{ - return ((1U << (nr & 15)) & (((const volatile unsigned short *) vaddr)[nr >> 4])) != 0; + const unsigned short *p = vaddr; + return (p[nr >> 4] & (1U << (nr & 15))) != 0; } /* Bitmap functions for the ext2 filesystem. */ -static inline int ext2_set_bit(int nr, volatile void *vaddr) -{ - char retval; - - __asm__ __volatile__ ("bfset %2{%1,#1}; sne %0" - : "=d" (retval) : "d" (nr^7), "m" (*(volatile char *) vaddr) : "memory"); - - return retval; -} - -static inline int ext2_clear_bit(int nr, volatile void *vaddr) -{ - char retval; - - __asm__ __volatile__ ("bfclr %2{%1,#1}; sne %0" - : "=d" (retval) : "d" (nr^7), "m" (*(volatile char *) vaddr) : "memory"); - - return retval; -} +#define ext2_set_bit(nr, addr) test_and_set_bit((nr) ^ 24, (unsigned long *)(addr)) +#define ext2_set_bit_atomic(lock, nr, addr) test_and_set_bit((nr) ^ 24, (unsigned long *)(addr)) +#define ext2_clear_bit(nr, addr) test_and_clear_bit((nr) ^ 24, (unsigned long *)(addr)) +#define ext2_clear_bit_atomic(lock, nr, addr) test_and_clear_bit((nr) ^ 24, (unsigned long *)(addr)) -#define ext2_set_bit_atomic(lock, nr, addr) \ - ({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_set_bit((nr), (addr)); \ - spin_unlock(lock); \ - ret; \ - }) - -#define ext2_clear_bit_atomic(lock, nr, addr) \ - ({ \ - int ret; \ - spin_lock(lock); \ - ret = ext2_clear_bit((nr), (addr)); \ - spin_unlock(lock); \ - ret; \ - }) - -static inline int ext2_test_bit(int nr, const volatile void *vaddr) +static inline int ext2_test_bit(int nr, const void *vaddr) { - return ((1U << (nr & 7)) & (((const volatile unsigned char *) vaddr)[nr >> 3])) != 0; + const unsigned char *p = vaddr; + return (p[nr >> 3] & (1U << (nr & 7))) != 0; } static inline int ext2_find_first_zero_bit(const void *vaddr, unsigned size) diff --git a/include/asm-m68k/page.h b/include/asm-m68k/page.h index 6b7d6a040b49..99a516709210 100644 --- a/include/asm-m68k/page.h +++ b/include/asm-m68k/page.h @@ -52,15 +52,13 @@ static inline void copy_page(void *to, void *from) static inline void clear_page(void *page) { - unsigned long data, tmp; - void *sp = page; + unsigned long tmp; + unsigned long *sp = page; - data = 0; - - *((unsigned long *)(page))++ = 0; - *((unsigned long *)(page))++ = 0; - *((unsigned long *)(page))++ = 0; - *((unsigned long *)(page))++ = 0; + *sp++ = 0; + *sp++ = 0; + *sp++ = 0; + *sp++ = 0; __asm__ __volatile__("1:\t" ".chip 68040\n\t" @@ -69,8 +67,8 @@ static inline void clear_page(void *page) "subqw #8,%2\n\t" "subqw #8,%2\n\t" "dbra %1,1b\n\t" - : "=a" (page), "=d" (tmp) - : "a" (sp), "0" (page), + : "=a" (sp), "=d" (tmp) + : "a" (page), "0" (sp), "1" ((PAGE_SIZE - 16) / 16 - 1)); } diff --git a/include/asm-m68k/string.h b/include/asm-m68k/string.h index 7ea5dc8e78db..44def078132a 100644 --- a/include/asm-m68k/string.h +++ b/include/asm-m68k/string.h @@ -290,9 +290,7 @@ static inline void * __memset_g(void * s, int c, size_t count) static inline void * __memset_page(void * s,int c,size_t count) { unsigned long data, tmp; - void *xs, *sp; - - xs = sp = s; + void *xs = s; c = c & 255; data = c | (c << 8); @@ -303,10 +301,11 @@ static inline void * __memset_page(void * s,int c,size_t count) if (((unsigned long) s) & 0x0f) __memset_g(s, c, count); else{ - *((unsigned long *)(s))++ = data; - *((unsigned long *)(s))++ = data; - *((unsigned long *)(s))++ = data; - *((unsigned long *)(s))++ = data; + unsigned long *sp = s; + *sp++ = data; + *sp++ = data; + *sp++ = data; + *sp++ = data; __asm__ __volatile__("1:\t" ".chip 68040\n\t" @@ -315,8 +314,8 @@ static inline void * __memset_page(void * s,int c,size_t count) "subqw #8,%2\n\t" "subqw #8,%2\n\t" "dbra %1,1b\n\t" - : "=a" (s), "=d" (tmp) - : "a" (sp), "0" (s), "1" ((count - 16) / 16 - 1) + : "=a" (sp), "=d" (tmp) + : "a" (s), "0" (sp), "1" ((count - 16) / 16 - 1) ); } -- cgit v1.2.3 From f51dc7a2a47aff78a05d65b6affccebd35fb68de Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sat, 19 Jun 2004 20:38:35 -0700 Subject: [PATCH] affs remount fixes AFFS: Fix oops on write after remount (from Roman Zippel): - Allocate/free bitmap as necessary - Remove last uses of SF_READONLY Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Torvalds --- fs/affs/amigaffs.c | 1 - fs/affs/bitmap.c | 29 +++++++++++++++++++++-------- fs/affs/super.c | 26 ++++++++++++-------------- include/linux/affs_fs.h | 3 ++- include/linux/affs_fs_sb.h | 1 - 5 files changed, 35 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/fs/affs/amigaffs.c b/fs/affs/amigaffs.c index b5d1aabf1c05..ddd53ec12a92 100644 --- a/fs/affs/amigaffs.c +++ b/fs/affs/amigaffs.c @@ -458,7 +458,6 @@ affs_error(struct super_block *sb, const char *function, const char *fmt, ...) if (!(sb->s_flags & MS_RDONLY)) printk(KERN_WARNING "AFFS: Remounting filesystem read-only\n"); sb->s_flags |= MS_RDONLY; - AFFS_SB(sb)->s_flags |= SF_READONLY; /* Don't allow to remount rw */ } void diff --git a/fs/affs/bitmap.c b/fs/affs/bitmap.c index 095ef77a407e..93d32d18e97f 100644 --- a/fs/affs/bitmap.c +++ b/fs/affs/bitmap.c @@ -272,8 +272,7 @@ err_full: return 0; } -int -affs_init_bitmap(struct super_block *sb) +int affs_init_bitmap(struct super_block *sb, int *flags) { struct affs_bm_info *bm; struct buffer_head *bmap_bh = NULL, *bh = NULL; @@ -282,13 +281,13 @@ affs_init_bitmap(struct super_block *sb) int i, res = 0; struct affs_sb_info *sbi = AFFS_SB(sb); - if (sb->s_flags & MS_RDONLY) + if (*flags & MS_RDONLY) return 0; if (!AFFS_ROOT_TAIL(sb, sbi->s_root_bh)->bm_flag) { printk(KERN_NOTICE "AFFS: Bitmap invalid - mounting %s read only\n", sb->s_id); - sb->s_flags |= MS_RDONLY; + *flags |= MS_RDONLY; return 0; } @@ -301,7 +300,7 @@ affs_init_bitmap(struct super_block *sb) bm = sbi->s_bitmap = kmalloc(size, GFP_KERNEL); if (!sbi->s_bitmap) { printk(KERN_ERR "AFFS: Bitmap allocation failed\n"); - return 1; + return -ENOMEM; } memset(sbi->s_bitmap, 0, size); @@ -316,13 +315,13 @@ affs_init_bitmap(struct super_block *sb) bh = affs_bread(sb, bm->bm_key); if (!bh) { printk(KERN_ERR "AFFS: Cannot read bitmap\n"); - res = 1; + res = -EIO; goto out; } if (affs_checksum_block(sb, bh)) { printk(KERN_WARNING "AFFS: Bitmap %u invalid - mounting %s read only.\n", bm->bm_key, sb->s_id); - sb->s_flags |= MS_RDONLY; + *flags |= MS_RDONLY; goto out; } pr_debug("AFFS: read bitmap block %d: %d\n", blk, bm->bm_key); @@ -338,7 +337,7 @@ affs_init_bitmap(struct super_block *sb) bmap_bh = affs_bread(sb, be32_to_cpu(bmap_blk[blk])); if (!bmap_bh) { printk(KERN_ERR "AFFS: Cannot read bitmap extension\n"); - res = 1; + res = -EIO; goto out; } bmap_blk = (u32 *)bmap_bh->b_data; @@ -383,3 +382,17 @@ out: affs_brelse(bmap_bh); return res; } + +void affs_free_bitmap(struct super_block *sb) +{ + struct affs_sb_info *sbi = AFFS_SB(sb); + + if (!sbi->s_bitmap) + return; + + affs_brelse(sbi->s_bmap_bh); + sbi->s_bmap_bh = NULL; + sbi->s_last_bmap = ~0; + kfree(sbi->s_bitmap); + sbi->s_bitmap = NULL; +} diff --git a/fs/affs/super.c b/fs/affs/super.c index ad83ec3a9a45..f4ebbd27ef1b 100644 --- a/fs/affs/super.c +++ b/fs/affs/super.c @@ -51,10 +51,9 @@ affs_put_super(struct super_block *sb) mark_buffer_dirty(sbi->s_root_bh); } - affs_brelse(sbi->s_bmap_bh); if (sbi->s_prefix) kfree(sbi->s_prefix); - kfree(sbi->s_bitmap); + affs_free_bitmap(sb); affs_brelse(sbi->s_root_bh); kfree(sbi); sb->s_fs_info = NULL; @@ -288,6 +287,7 @@ static int affs_fill_super(struct super_block *sb, void *data, int silent) gid_t gid; int reserved; unsigned long mount_flags; + int tmp_flags; /* fix remount prototype... */ pr_debug("AFFS: read_super(%s)\n",data ? (const char *)data : "no options"); @@ -399,7 +399,6 @@ got_root: printk(KERN_NOTICE "AFFS: Dircache FS - mounting %s read only\n", sb->s_id); sb->s_flags |= MS_RDONLY; - sbi->s_flags |= SF_READONLY; } switch (chksum) { case MUFS_FS: @@ -455,8 +454,10 @@ got_root: sbi->s_root_bh = root_bh; /* N.B. after this point s_root_bh must be released */ - if (affs_init_bitmap(sb)) + tmp_flags = sb->s_flags; + if (affs_init_bitmap(sb, &tmp_flags)) goto out_error; + sb->s_flags = tmp_flags; /* set up enough so that it can read an inode */ @@ -498,7 +499,7 @@ affs_remount(struct super_block *sb, int *flags, char *data) int reserved; int root_block; unsigned long mount_flags; - unsigned long read_only = sbi->s_flags & SF_READONLY; + int res = 0; pr_debug("AFFS: remount(flags=0x%x,opts=\"%s\")\n",*flags,data); @@ -507,7 +508,7 @@ affs_remount(struct super_block *sb, int *flags, char *data) if (!parse_options(data,&uid,&gid,&mode,&reserved,&root_block, &blocksize,&sbi->s_prefix,sbi->s_volume,&mount_flags)) return -EINVAL; - sbi->s_flags = mount_flags | read_only; + sbi->s_flags = mount_flags; sbi->s_mode = mode; sbi->s_uid = uid; sbi->s_gid = gid; @@ -518,14 +519,11 @@ affs_remount(struct super_block *sb, int *flags, char *data) sb->s_dirt = 1; while (sb->s_dirt) affs_write_super(sb); - sb->s_flags |= MS_RDONLY; - } else if (!(sbi->s_flags & SF_READONLY)) { - sb->s_flags &= ~MS_RDONLY; - } else { - affs_warning(sb,"remount","Cannot remount fs read/write because of errors"); - return -EINVAL; - } - return 0; + affs_free_bitmap(sb); + } else + res = affs_init_bitmap(sb, flags); + + return res; } static int diff --git a/include/linux/affs_fs.h b/include/linux/affs_fs.h index c849309b1131..5ba9d6205dc0 100644 --- a/include/linux/affs_fs.h +++ b/include/linux/affs_fs.h @@ -36,7 +36,8 @@ extern u32 affs_count_free_bits(u32 blocksize, const void *data); extern u32 affs_count_free_blocks(struct super_block *s); extern void affs_free_block(struct super_block *sb, u32 block); extern u32 affs_alloc_block(struct inode *inode, u32 goal); -extern int affs_init_bitmap(struct super_block *sb); +extern int affs_init_bitmap(struct super_block *sb, int *flags); +extern void affs_free_bitmap(struct super_block *sb); /* namei.c */ diff --git a/include/linux/affs_fs_sb.h b/include/linux/affs_fs_sb.h index d2f86715cf1c..d722befe1ced 100644 --- a/include/linux/affs_fs_sb.h +++ b/include/linux/affs_fs_sb.h @@ -47,7 +47,6 @@ struct affs_sb_info { #define SF_OFS 0x0200 /* Old filesystem */ #define SF_PREFIX 0x0400 /* Buffer for prefix is allocated */ #define SF_VERBOSE 0x0800 /* Talk about fs when mounting */ -#define SF_READONLY 0x1000 /* Don't allow to remount rw */ /* short cut to get to the affs specific sb data */ static inline struct affs_sb_info *AFFS_SB(struct super_block *sb) -- cgit v1.2.3 From 482bfe2cd44b9132cadff1a07f82a44d2b7944d1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sat, 19 Jun 2004 20:39:19 -0700 Subject: [PATCH] m68k: atomic op fixups M68k: Add missing atomic operations (from Roman Zippel and me) and replace `__inline__' by `inline' while we're at it. Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Torvalds --- include/asm-m68k/atomic.h | 98 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 91 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/include/asm-m68k/atomic.h b/include/asm-m68k/atomic.h index 6909d9694c67..3f867738062a 100644 --- a/include/asm-m68k/atomic.h +++ b/include/asm-m68k/atomic.h @@ -1,6 +1,8 @@ #ifndef __ARCH_M68K_ATOMIC__ #define __ARCH_M68K_ATOMIC__ +#include /* local_irq_XXX() */ + /* * Atomic operations that C can't guarantee us. Useful for * resource counting etc.. @@ -16,39 +18,121 @@ typedef struct { int counter; } atomic_t; #define atomic_read(v) ((v)->counter) #define atomic_set(v, i) (((v)->counter) = i) -static __inline__ void atomic_add(int i, atomic_t *v) +static inline void atomic_add(int i, atomic_t *v) { __asm__ __volatile__("addl %1,%0" : "+m" (*v) : "id" (i)); } -static __inline__ void atomic_sub(int i, atomic_t *v) +static inline void atomic_sub(int i, atomic_t *v) { __asm__ __volatile__("subl %1,%0" : "+m" (*v) : "id" (i)); } -static __inline__ void atomic_inc(atomic_t *v) +static inline void atomic_inc(atomic_t *v) { __asm__ __volatile__("addql #1,%0" : "+m" (*v)); } -static __inline__ void atomic_dec(atomic_t *v) +static inline void atomic_dec(atomic_t *v) { __asm__ __volatile__("subql #1,%0" : "+m" (*v)); } -static __inline__ int atomic_dec_and_test(atomic_t *v) +static inline int atomic_dec_and_test(atomic_t *v) { char c; __asm__ __volatile__("subql #1,%1; seq %0" : "=d" (c), "+m" (*v)); return c != 0; } -static __inline__ void atomic_clear_mask(unsigned long mask, unsigned long *v) +static inline int atomic_inc_and_test(atomic_t *v) +{ + char c; + __asm__ __volatile__("addql #1,%1; seq %0" : "=d" (c), "+m" (*v)); + return c != 0; +} + +#ifdef CONFIG_RMW_INSNS +static inline int atomic_add_return(int i, atomic_t *v) +{ + int t, tmp; + + __asm__ __volatile__( + "1: movel %2,%1\n" + " addl %3,%1\n" + " casl %2,%1,%0\n" + " jne 1b" + : "+m" (*v), "=&d" (t), "=&d" (tmp) + : "g" (i), "2" (atomic_read(v))); + return t; +} + +static inline int atomic_sub_return(int i, atomic_t *v) +{ + int t, tmp; + + __asm__ __volatile__( + "1: movel %2,%1\n" + " subl %3,%1\n" + " casl %2,%1,%0\n" + " jne 1b" + : "+m" (*v), "=&d" (t), "=&d" (tmp) + : "g" (i), "2" (atomic_read(v))); + return t; +} +#else /* !CONFIG_RMW_INSNS */ +static inline int atomic_add_return(int i, atomic_t * v) +{ + unsigned long flags; + int t; + + local_irq_save(flags); + t = atomic_read(v); + t += i; + atomic_set(v, t); + local_irq_restore(flags); + + return t; +} + +static inline int atomic_sub_return(int i, atomic_t * v) +{ + unsigned long flags; + int t; + + local_irq_save(flags); + t = atomic_read(v); + t -= i; + atomic_set(v, t); + local_irq_restore(flags); + + return t; +} +#endif /* !CONFIG_RMW_INSNS */ + +#define atomic_dec_return(v) atomic_sub_return(1, (v)) +#define atomic_inc_return(v) atomic_add_return(1, (v)) + +static inline int atomic_sub_and_test(int i, atomic_t *v) +{ + char c; + __asm__ __volatile__("subl %2,%1; seq %0" : "=d" (c), "+m" (*v): "g" (i)); + return c != 0; +} + +static inline int atomic_add_negative(int i, atomic_t *v) +{ + char c; + __asm__ __volatile__("addl %2,%1; smi %0" : "=d" (c), "+m" (*v): "g" (i)); + return c != 0; +} + +static inline void atomic_clear_mask(unsigned long mask, unsigned long *v) { __asm__ __volatile__("andl %1,%0" : "+m" (*v) : "id" (~(mask))); } -static __inline__ void atomic_set_mask(unsigned long mask, unsigned long *v) +static inline void atomic_set_mask(unsigned long mask, unsigned long *v) { __asm__ __volatile__("orl %1,%0" : "+m" (*v) : "id" (mask)); } -- cgit v1.2.3 From 85bed77bb232e5ee7ce6dccaf7c85ec656ebf9c9 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sat, 19 Jun 2004 20:39:30 -0700 Subject: [PATCH] m68k: I/O abstraction updates M68k I/O abstraction updates: - Make I/O ports and addresses `unsigned long' - Add casts to make operations warning-compatible with other archs - Add {in,out}[wl]_p() and {in,out}l(), which are needed for some drivers Signed-off-by: Geert Uytterhoeven Signed-off-by: Linus Torvalds --- include/asm-m68k/io.h | 46 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 14 deletions(-) (limited to 'include') diff --git a/include/asm-m68k/io.h b/include/asm-m68k/io.h index aa6fffff2084..d32a489f4800 100644 --- a/include/asm-m68k/io.h +++ b/include/asm-m68k/io.h @@ -120,7 +120,7 @@ extern int isa_sex; * be compiled in so the case statement will be optimised away */ -static inline u8 *isa_itb(long addr) +static inline u8 *isa_itb(unsigned long addr) { switch(ISA_TYPE) { @@ -136,7 +136,7 @@ static inline u8 *isa_itb(long addr) default: return 0; /* avoid warnings, just in case */ } } -static inline u16 *isa_itw(long addr) +static inline u16 *isa_itw(unsigned long addr) { switch(ISA_TYPE) { @@ -152,7 +152,7 @@ static inline u16 *isa_itw(long addr) default: return 0; /* avoid warnings, just in case */ } } -static inline u8 *isa_mtb(long addr) +static inline u8 *isa_mtb(unsigned long addr) { switch(ISA_TYPE) { @@ -168,7 +168,7 @@ static inline u8 *isa_mtb(long addr) default: return 0; /* avoid warnings, just in case */ } } -static inline u16 *isa_mtw(long addr) +static inline u16 *isa_mtw(unsigned long addr) { switch(ISA_TYPE) { @@ -191,10 +191,14 @@ static inline u16 *isa_mtw(long addr) #define isa_outb(val,port) out_8(isa_itb(port),(val)) #define isa_outw(val,port) (ISA_SEX ? out_be16(isa_itw(port),(val)) : out_le16(isa_itw(port),(val))) -#define isa_readb(p) in_8(isa_mtb(p)) -#define isa_readw(p) (ISA_SEX ? in_be16(isa_mtw(p)) : in_le16(isa_mtw(p))) -#define isa_writeb(val,p) out_8(isa_mtb(p),(val)) -#define isa_writew(val,p) (ISA_SEX ? out_be16(isa_mtw(p),(val)) : out_le16(isa_mtw(p),(val))) +#define isa_readb(p) in_8(isa_mtb((unsigned long)(p))) +#define isa_readw(p) \ + (ISA_SEX ? in_be16(isa_mtw((unsigned long)(p))) \ + : in_le16(isa_mtw((unsigned long)(p)))) +#define isa_writeb(val,p) out_8(isa_mtb((unsigned long)(p)),(val)) +#define isa_writew(val,p) \ + (ISA_SEX ? out_be16(isa_mtw((unsigned long)(p)),(val)) \ + : out_le16(isa_mtw((unsigned long)(p)),(val))) static inline void isa_delay(void) { @@ -215,17 +219,21 @@ static inline void isa_delay(void) #define isa_inb_p(p) ({u8 v=isa_inb(p);isa_delay();v;}) #define isa_outb_p(v,p) ({isa_outb((v),(p));isa_delay();}) +#define isa_inw_p(p) ({u16 v=isa_inw(p);isa_delay();v;}) +#define isa_outw_p(v,p) ({isa_outw((v),(p));isa_delay();}) +#define isa_inl_p(p) ({u32 v=isa_inl(p);isa_delay();v;}) +#define isa_outl_p(v,p) ({isa_outl((v),(p));isa_delay();}) -#define isa_insb(port, buf, nr) raw_insb(isa_itb(port), (buf), (nr)) -#define isa_outsb(port, buf, nr) raw_outsb(isa_itb(port), (buf), (nr)) +#define isa_insb(port, buf, nr) raw_insb(isa_itb(port), (u8 *)(buf), (nr)) +#define isa_outsb(port, buf, nr) raw_outsb(isa_itb(port), (u8 *)(buf), (nr)) #define isa_insw(port, buf, nr) \ - (ISA_SEX ? raw_insw(isa_itw(port), (buf), (nr)) : \ - raw_insw_swapw(isa_itw(port), (buf), (nr))) + (ISA_SEX ? raw_insw(isa_itw(port), (u16 *)(buf), (nr)) : \ + raw_insw_swapw(isa_itw(port), (u16 *)(buf), (nr))) #define isa_outsw(port, buf, nr) \ - (ISA_SEX ? raw_outsw(isa_itw(port), (buf), (nr)) : \ - raw_outsw_swapw(isa_itw(port), (buf), (nr))) + (ISA_SEX ? raw_outsw(isa_itw(port), (u16 *)(buf), (nr)) : \ + raw_outsw_swapw(isa_itw(port), (u16 *)(buf), (nr))) #endif /* CONFIG_ISA */ @@ -235,9 +243,13 @@ static inline void isa_delay(void) #define outb isa_outb #define outb_p isa_outb_p #define inw isa_inw +#define inw_p isa_inw_p #define outw isa_outw +#define outw_p isa_outw_p #define inl isa_inw +#define inl_p isa_inw_p #define outl isa_outw +#define outl_p isa_outw_p #define insb isa_insb #define insw isa_insw #define outsb isa_outsb @@ -281,10 +293,16 @@ static inline void isa_delay(void) #define inb(port) ((port)<1024 ? isa_inb(port) : in_8(port)) #define inb_p(port) ((port)<1024 ? isa_inb_p(port) : in_8(port)) #define inw(port) ((port)<1024 ? isa_inw(port) : in_le16(port)) +#define inw_p(port) ((port)<1024 ? isa_inw_p(port) : in_le16(port)) +#define inl(port) ((port)<1024 ? isa_inl(port) : in_le32(port)) +#define inl_p(port) ((port)<1024 ? isa_inl_p(port) : in_le32(port)) #define outb(val,port) ((port)<1024 ? isa_outb((val),(port)) : out_8((port),(val))) #define outb_p(val,port) ((port)<1024 ? isa_outb_p((val),(port)) : out_8((port),(val))) #define outw(val,port) ((port)<1024 ? isa_outw((val),(port)) : out_le16((port),(val))) +#define outw_p(val,port) ((port)<1024 ? isa_outw_p((val),(port)) : out_le16((port),(val))) +#define outl(val,port) ((port)<1024 ? isa_outl((val),(port)) : out_le32((port),(val))) +#define outl_p(val,port) ((port)<1024 ? isa_outl_p((val),(port)) : out_le32((port),(val))) #endif #endif /* CONFIG_PCI */ -- cgit v1.2.3 From 86adf644850fbbc3da6448e8e1a0e39fe755e8e9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 20 Jun 2004 13:45:03 -0300 Subject: [NET] move skb_can_coalesce to skbuff.h This one also removes the duplicate can_coalesce in tcp.c and makes it use skb_can_coalesce. Signed-off-by: Arnaldo Carvalho de Melo --- include/linux/skbuff.h | 12 ++++++++++++ net/ipv4/ip_output.c | 11 ----------- net/ipv4/tcp.c | 15 ++------------- 3 files changed, 14 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index f777878608ab..cc44268a6e83 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -993,6 +993,18 @@ static inline int skb_add_data(struct sk_buff *skb, return -EFAULT; } +static inline int skb_can_coalesce(struct sk_buff *skb, int i, + struct page *page, int off) +{ + if (i) { + struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i - 1]; + + return page == frag->page && + off == frag->page_offset + frag->size; + } + return 0; +} + /** * skb_linearize - convert paged skb to linear one * @skb: buffer to linarize diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 60fc5091c0ef..34a4a3feccb7 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -702,17 +702,6 @@ ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk return 0; } -static inline int -skb_can_coalesce(struct sk_buff *skb, int i, struct page *page, int off) -{ - if (i) { - skb_frag_t *frag = &skb_shinfo(skb)->frags[i-1]; - return page == frag->page && - off == frag->page_offset+frag->size; - } - return 0; -} - static inline unsigned int csum_page(struct page *page, int offset, int copy) { diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 0b29f833d29b..23532b96e85b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -737,17 +737,6 @@ do_interrupted: goto out; } -static inline int can_coalesce(struct sk_buff *skb, int i, struct page *page, - int off) -{ - if (i) { - skb_frag_t *frag = &skb_shinfo(skb)->frags[i - 1]; - return page == frag->page && - off == frag->page_offset + frag->size; - } - return 0; -} - static inline void fill_page_desc(struct sk_buff *skb, int i, struct page *page, int off, int size) { @@ -865,7 +854,7 @@ new_segment: copy = size; i = skb_shinfo(skb)->nr_frags; - if (can_coalesce(skb, i, page, offset)) { + if (skb_can_coalesce(skb, i, page, offset)) { skb_shinfo(skb)->frags[i - 1].size += copy; } else if (i < MAX_SKB_FRAGS) { get_page(page); @@ -1053,7 +1042,7 @@ new_segment: struct page *page = TCP_PAGE(sk); int off = TCP_OFF(sk); - if (can_coalesce(skb, i, page, off) && + if (skb_can_coalesce(skb, i, page, off) && off != PAGE_SIZE) { /* We can extend the last page * fragment. */ -- cgit v1.2.3 From 4455194734b64ead30f60545e2d37eb7e091c6fb Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Sun, 20 Jun 2004 14:54:15 -0300 Subject: [NET] move tcp_memory_free to sk_stream_memory_free Signed-off-by: Arnaldo Carvalho de Melo --- include/net/sock.h | 5 +++++ net/ipv4/tcp.c | 15 +++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/include/net/sock.h b/include/net/sock.h index 5624b084742f..0ee6ccddca40 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -429,6 +429,11 @@ static inline int sk_stream_wspace(struct sock *sk) extern void sk_stream_write_space(struct sock *sk); +static inline int sk_stream_memory_free(struct sock *sk) +{ + return sk->sk_wmem_queued < sk->sk_sndbuf; +} + /* The per-socket spinlock must be held here. */ #define sk_add_backlog(__sk, __skb) \ do { if (!(__sk)->sk_backlog.tail) { \ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 23532b96e85b..476716f4bc30 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -670,11 +670,6 @@ static int wait_for_tcp_connect(struct sock *sk, int flags, long *timeo_p) return 0; } -static inline int tcp_memory_free(struct sock *sk) -{ - return sk->sk_wmem_queued < sk->sk_sndbuf; -} - /* * Wait for more memory for a socket */ @@ -686,7 +681,7 @@ static int wait_for_tcp_memory(struct sock *sk, long *timeo) long current_timeo = *timeo; DEFINE_WAIT(wait); - if (tcp_memory_free(sk)) + if (sk_stream_memory_free(sk)) current_timeo = vm_wait = (net_random() % (HZ / 5)) + 2; for (;;) { @@ -701,13 +696,13 @@ static int wait_for_tcp_memory(struct sock *sk, long *timeo) if (signal_pending(current)) goto do_interrupted; clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags); - if (tcp_memory_free(sk) && !vm_wait) + if (sk_stream_memory_free(sk) && !vm_wait) break; set_bit(SOCK_NOSPACE, &sk->sk_socket->flags); tp->write_pending++; release_sock(sk); - if (!tcp_memory_free(sk) || vm_wait) + if (!sk_stream_memory_free(sk) || vm_wait) current_timeo = schedule_timeout(current_timeo); lock_sock(sk); tp->write_pending--; @@ -838,7 +833,7 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page **pages, int poffse if (!tp->send_head || (copy = mss_now - skb->len) <= 0) { new_segment: - if (!tcp_memory_free(sk)) + if (!sk_stream_memory_free(sk)) goto wait_for_sndbuf; skb = tcp_alloc_pskb(sk, 0, tp->mss_cache, @@ -1005,7 +1000,7 @@ new_segment: /* Allocate new segment. If the interface is SG, * allocate skb fitting to single page. */ - if (!tcp_memory_free(sk)) + if (!sk_stream_memory_free(sk)) goto wait_for_sndbuf; skb = tcp_alloc_pskb(sk, select_size(sk, tp), -- cgit v1.2.3 From 567f48727b6cbd928c1d8531717ea981da164730 Mon Sep 17 00:00:00 2001 From: Arthur Kepner Date: Sun, 20 Jun 2004 03:36:21 -0700 Subject: [NET]: Lockless loopback patch (version 2). --- drivers/net/loopback.c | 41 ++++++++++++++++++++++++++++------- include/linux/netdevice.h | 1 + include/net/pkt_sched.h | 2 ++ net/core/dev.c | 55 ++++++++++++++++++++++++++++++++++------------- net/sched/sch_api.c | 3 +++ net/sched/sch_generic.c | 43 ++++++++++++++++++++++++------------ 6 files changed, 108 insertions(+), 37 deletions(-) (limited to 'include') diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 1885bfe3c959..53dc879651e4 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -56,6 +56,7 @@ #include #include +static struct net_device_stats *loopback_stats; #define LOOPBACK_OVERHEAD (128 + MAX_HEADER + 16 + 16) @@ -123,7 +124,6 @@ static void emulate_large_send_offload(struct sk_buff *skb) */ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev) { - struct net_device_stats *stats = dev->priv; skb_orphan(skb); @@ -142,11 +142,12 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev) } dev->last_rx = jiffies; - if (likely(stats)) { - stats->rx_bytes+=skb->len; - stats->tx_bytes+=skb->len; - stats->rx_packets++; - stats->tx_packets++; + if (likely(loopback_stats)) { + get_cpu_ptr(loopback_stats)->rx_bytes += skb->len; + get_cpu_ptr(loopback_stats)->tx_bytes += skb->len; + get_cpu_ptr(loopback_stats)->rx_packets++; + get_cpu_ptr(loopback_stats)->tx_packets++; + put_cpu_ptr(loopback_stats); } netif_rx(skb); @@ -156,7 +157,28 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev) static struct net_device_stats *get_stats(struct net_device *dev) { - return (struct net_device_stats *)dev->priv; + struct net_device_stats *stats = dev->priv; + int i; + + if (!stats) { + return NULL; + } + + memset(stats, 0, sizeof(struct net_device_stats)); + if (!loopback_stats) { + return stats; + } + + for (i=0; i < NR_CPUS; i++) { + if (!cpu_possible(i)) + continue; + stats->rx_bytes += per_cpu_ptr(loopback_stats, i)->rx_bytes; + stats->tx_bytes += per_cpu_ptr(loopback_stats, i)->tx_bytes; + stats->rx_packets += per_cpu_ptr(loopback_stats, i)->rx_packets; + stats->tx_packets += per_cpu_ptr(loopback_stats, i)->tx_packets; + } + + return stats; } struct net_device loopback_dev = { @@ -173,7 +195,8 @@ struct net_device loopback_dev = { .rebuild_header = eth_rebuild_header, .flags = IFF_LOOPBACK, .features = NETIF_F_SG|NETIF_F_FRAGLIST - |NETIF_F_NO_CSUM|NETIF_F_HIGHDMA, + |NETIF_F_NO_CSUM|NETIF_F_HIGHDMA + |NETIF_F_LLTX, }; /* Setup and register the of the LOOPBACK device. */ @@ -188,6 +211,8 @@ int __init loopback_init(void) loopback_dev.priv = stats; loopback_dev.get_stats = &get_stats; } + + loopback_stats = alloc_percpu(struct net_device_stats); return register_netdev(&loopback_dev); }; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index a55f97a34035..97758cd8f50e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -405,6 +405,7 @@ struct net_device #define NETIF_F_HW_VLAN_FILTER 512 /* Receive filtering on VLAN */ #define NETIF_F_VLAN_CHALLENGED 1024 /* Device cannot handle VLAN packets */ #define NETIF_F_TSO 2048 /* Can offload TCP/IP segmentation */ +#define NETIF_F_LLTX 4096 /* LockLess TX */ /* Called after device is detached from network. */ void (*uninit)(struct net_device *dev); diff --git a/include/net/pkt_sched.h b/include/net/pkt_sched.h index 80661d855fd8..d8960a01df42 100644 --- a/include/net/pkt_sched.h +++ b/include/net/pkt_sched.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #ifdef CONFIG_X86_TSC @@ -92,6 +93,7 @@ struct Qdisc struct net_device *dev; struct tc_stats stats; + struct rcu_head q_rcu; int (*reshape_fail)(struct sk_buff *skb, struct Qdisc *q); /* This field is deprecated, but it is still used by CBQ diff --git a/net/core/dev.c b/net/core/dev.c index 27cbb530b95b..9729210ca4cb 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -107,6 +107,7 @@ #include #include #include +#include #ifdef CONFIG_NET_RADIO #include /* Note : will define WIRELESS_EXT */ #include @@ -1305,6 +1306,20 @@ int __skb_linearize(struct sk_buff *skb, int gfp_mask) return 0; } +#define HARD_TX_LOCK_BH(dev, cpu) { \ + if ((dev->features & NETIF_F_LLTX) == 0) { \ + spin_lock_bh(&dev->xmit_lock); \ + dev->xmit_lock_owner = cpu; \ + } \ +} + +#define HARD_TX_UNLOCK_BH(dev) { \ + if ((dev->features & NETIF_F_LLTX) == 0) { \ + dev->xmit_lock_owner = -1; \ + spin_unlock_bh(&dev->xmit_lock); \ + } \ +} + /** * dev_queue_xmit - transmit a buffer * @skb: buffer to transmit @@ -1348,18 +1363,35 @@ int dev_queue_xmit(struct sk_buff *skb) if (skb_checksum_help(&skb, 0)) goto out_kfree_skb; - /* Grab device queue */ - spin_lock_bh(&dev->queue_lock); + rcu_read_lock(); + /* Updates of qdisc are serialized by queue_lock. + * The struct Qdisc which is pointed to by qdisc is now a + * rcu structure - it may be accessed without acquiring + * a lock (but the structure may be stale.) The freeing of the + * qdisc will be deferred until it's known that there are no + * more references to it. + * + * If the qdisc has an enqueue function, we still need to + * hold the queue_lock before calling it, since queue_lock + * also serializes access to the device queue. + */ + q = dev->qdisc; + smp_read_barrier_depends(); if (q->enqueue) { + /* Grab device queue */ + spin_lock_bh(&dev->queue_lock); + rc = q->enqueue(skb, q); qdisc_run(dev); spin_unlock_bh(&dev->queue_lock); + rcu_read_unlock(); rc = rc == NET_XMIT_BYPASS ? NET_XMIT_SUCCESS : rc; goto out; } + rcu_read_unlock(); /* The device has no queue. Common case for software devices: loopback, all the sorts of tunnels... @@ -1374,17 +1406,12 @@ int dev_queue_xmit(struct sk_buff *skb) Either shot noqueue qdisc, it is even simpler 8) */ if (dev->flags & IFF_UP) { + preempt_disable(); int cpu = smp_processor_id(); if (dev->xmit_lock_owner != cpu) { - /* - * The spin_lock effectivly does a preempt lock, but - * we are about to drop that... - */ - preempt_disable(); - spin_unlock(&dev->queue_lock); - spin_lock(&dev->xmit_lock); - dev->xmit_lock_owner = cpu; + + HARD_TX_LOCK_BH(dev, cpu); preempt_enable(); if (!netif_queue_stopped(dev)) { @@ -1393,18 +1420,17 @@ int dev_queue_xmit(struct sk_buff *skb) rc = 0; if (!dev->hard_start_xmit(skb, dev)) { - dev->xmit_lock_owner = -1; - spin_unlock_bh(&dev->xmit_lock); + HARD_TX_UNLOCK_BH(dev); goto out; } } - dev->xmit_lock_owner = -1; - spin_unlock_bh(&dev->xmit_lock); + HARD_TX_UNLOCK_BH(dev); if (net_ratelimit()) printk(KERN_CRIT "Virtual device %s asks to " "queue packet!\n", dev->name); goto out_enetdown; } else { + preempt_enable(); /* Recursion is detected! It is possible, * unfortunately */ if (net_ratelimit()) @@ -1412,7 +1438,6 @@ int dev_queue_xmit(struct sk_buff *skb) "%s, fix it urgently!\n", dev->name); } } - spin_unlock_bh(&dev->queue_lock); out_enetdown: rc = -ENETDOWN; out_kfree_skb: diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 432531dca1c8..85ed105d1a12 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -450,6 +450,9 @@ qdisc_create(struct net_device *dev, u32 handle, struct rtattr **tca, int *errp) if (!try_module_get(ops->owner)) goto err_out; + /* enqueue is accessed locklessly - make sure it's visible + * before we set a netdevice's qdisc pointer to sch */ + smp_wmb(); if (!ops->init || (err = ops->init(sch, tca[TCA_OPTIONS-1])) == 0) { write_lock(&qdisc_tree_lock); sch->next = dev->qdisc_list; diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 97b56255c7be..111dad476d2b 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -387,6 +388,9 @@ struct Qdisc * qdisc_create_dflt(struct net_device *dev, struct Qdisc_ops *ops) sch->dev = dev; sch->stats.lock = &dev->queue_lock; atomic_set(&sch->refcnt, 1); + /* enqueue is accessed locklessly - make sure it's visible + * before we set a netdevice's qdisc pointer to sch */ + smp_wmb(); if (!ops->init || ops->init(sch, NULL) == 0) return sch; @@ -404,18 +408,36 @@ void qdisc_reset(struct Qdisc *qdisc) ops->reset(qdisc); } +/* this is the rcu callback function to clean up a qdisc when there + * are no further references to it */ + +static void __qdisc_destroy (void * arg) +{ + struct Qdisc *qdisc = (struct Qdisc *) arg; + struct Qdisc_ops *ops = qdisc->ops; + +#ifdef CONFIG_NET_ESTIMATOR + qdisc_kill_estimator(&qdisc->stats); +#endif + if (ops->reset) + ops->reset(qdisc); + if (ops->destroy) + ops->destroy(qdisc); + module_put(ops->owner); + + if (!(qdisc->flags&TCQ_F_BUILTIN)) + kfree(qdisc); +} + /* Under dev->queue_lock and BH! */ void qdisc_destroy(struct Qdisc *qdisc) { - struct Qdisc_ops *ops = qdisc->ops; - struct net_device *dev; + struct net_device *dev = qdisc->dev; if (!atomic_dec_and_test(&qdisc->refcnt)) return; - dev = qdisc->dev; - if (dev) { struct Qdisc *q, **qp; for (qp = &qdisc->dev->qdisc_list; (q=*qp) != NULL; qp = &q->next) { @@ -425,16 +447,9 @@ void qdisc_destroy(struct Qdisc *qdisc) } } } -#ifdef CONFIG_NET_ESTIMATOR - qdisc_kill_estimator(&qdisc->stats); -#endif - if (ops->reset) - ops->reset(qdisc); - if (ops->destroy) - ops->destroy(qdisc); - module_put(ops->owner); - if (!(qdisc->flags&TCQ_F_BUILTIN)) - kfree(qdisc); + + call_rcu(&qdisc->q_rcu, __qdisc_destroy, qdisc); + } -- cgit v1.2.3 From 4f6d91e3ad4a3277604b704b20f324eb101e97e4 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 20 Jun 2004 04:52:03 -0700 Subject: [PATCH] ppc64 CONFIG_ALTIVEC=n build fix With CONFIG_ALTIVEC=n, flush_altivec_to_thread() has no implementation. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-ppc64/system.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/asm-ppc64/system.h b/include/asm-ppc64/system.h index 8d8a6f89c740..67ac484a14cd 100644 --- a/include/asm-ppc64/system.h +++ b/include/asm-ppc64/system.h @@ -112,13 +112,21 @@ extern int _get_PVR(void); extern void giveup_fpu(struct task_struct *); extern void disable_kernel_fp(void); extern void flush_fp_to_thread(struct task_struct *); -extern void flush_altivec_to_thread(struct task_struct *); extern void enable_kernel_fp(void); extern void giveup_altivec(struct task_struct *); extern void disable_kernel_altivec(void); extern void enable_kernel_altivec(void); extern void cvt_fd(float *from, double *to, unsigned long *fpscr); extern void cvt_df(double *from, float *to, unsigned long *fpscr); + +#ifdef CONFIG_ALTIVEC +extern void flush_altivec_to_thread(struct task_struct *); +#else +static inline void flush_altivec_to_thread(struct task_struct *t) +{ +} +#endif + extern int abs(int); extern struct task_struct *__switch_to(struct task_struct *, -- cgit v1.2.3 From c397fc82d28c5967dac641f34e0e798443aa6526 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Sun, 20 Jun 2004 04:52:14 -0700 Subject: [PATCH] ppc64: eeh.h warning-fix In file included from include/asm/io.h:365, from drivers/video/kyro/STG4000Reg.h:23, from drivers/video/kyro/STG4000Ramdac.c:16: include/asm/eeh.h:58: warning: `struct device_node' declared inside parameter list include/asm/eeh.h:58: warning: its scope is only this definition or declaration, which is probably not what you want Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/asm-ppc64/eeh.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/asm-ppc64/eeh.h b/include/asm-ppc64/eeh.h index 928dc5e39df5..3ca700cb04bc 100644 --- a/include/asm-ppc64/eeh.h +++ b/include/asm-ppc64/eeh.h @@ -55,6 +55,7 @@ void __init pci_addr_cache_build(void); * device (including config space i/o). Call eeh_add_device_late * to finish the eeh setup for this device. */ +struct device_node; void eeh_add_device_early(struct device_node *); void eeh_add_device_late(struct pci_dev *); -- cgit v1.2.3 From efd3d374a20b019c0c89b2050123f9bcfcdeb794 Mon Sep 17 00:00:00 2001 From: Andrey Panin Date: Sun, 20 Jun 2004 04:52:25 -0700 Subject: [PATCH] export DMI check functions This patch creates and exports 2 functions which can be used by the rest of kernel code to perform DMI data checks: - dmi_check_system() function checks system DMI data against given blacklist table and on each match runs corresponding callback function; - dmi_get_system_info() function returns DMI data value. Useful for people wanting more complex DMI data check than simple string match. Also filling unused match entries with NO_MATCH made optional, but existing NO_MATCH occurences are left intact, so people are free to continue dmi_scan.c patching without massive reject problems. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/kernel/dmi_scan.c | 111 ++++++++++++++++++++++---------------------- include/linux/dmi.h | 47 +++++++++++++++++++ 2 files changed, 102 insertions(+), 56 deletions(-) create mode 100644 include/linux/dmi.h (limited to 'include') diff --git a/arch/i386/kernel/dmi_scan.c b/arch/i386/kernel/dmi_scan.c index aafc78898f93..d3909ea20c5a 100644 --- a/arch/i386/kernel/dmi_scan.c +++ b/arch/i386/kernel/dmi_scan.c @@ -10,6 +10,7 @@ #include #include #include +#include #include unsigned long dmi_broken; @@ -139,21 +140,6 @@ static int __init dmi_iterate(void (*decode)(struct dmi_header *)) return -1; } - -enum -{ - DMI_BIOS_VENDOR, - DMI_BIOS_VERSION, - DMI_BIOS_DATE, - DMI_SYS_VENDOR, - DMI_PRODUCT_NAME, - DMI_PRODUCT_VERSION, - DMI_BOARD_VENDOR, - DMI_BOARD_NAME, - DMI_BOARD_VERSION, - DMI_STRING_MAX -}; - static char *dmi_ident[DMI_STRING_MAX]; /* @@ -176,26 +162,11 @@ static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string) } /* - * DMI callbacks for problem boards + * Ugly compatibility crap. */ - -struct dmi_strmatch -{ - u8 slot; - char *substr; -}; - -#define NONE 255 - -struct dmi_blacklist -{ - int (*callback)(struct dmi_blacklist *); - char *ident; - struct dmi_strmatch matches[4]; -}; - -#define NO_MATCH { NONE, NULL} -#define MATCH(a,b) { a, b } +#define dmi_blacklist dmi_system_id +#define NO_MATCH { DMI_NONE, NULL} +#define MATCH DMI_MATCH /* * Reboot options and system auto-detection code provided by @@ -1054,9 +1025,6 @@ static __initdata struct dmi_blacklist dmi_blacklist[]={ static __init void dmi_check_blacklist(void) { - struct dmi_blacklist *d; - int i; - #ifdef CONFIG_ACPI_BOOT #define ACPI_BLACKLIST_CUTOFF_YEAR 2001 @@ -1078,25 +1046,7 @@ static __init void dmi_check_blacklist(void) } } #endif - - d=&dmi_blacklist[0]; - while(d->callback) - { - for(i=0;i<4;i++) - { - int s = d->matches[i].slot; - if(s==NONE) - continue; - if(dmi_ident[s] && strstr(dmi_ident[s], d->matches[i].substr)) - continue; - /* No match */ - goto fail; - } - if(d->callback(d)) - return; -fail: - d++; - } + dmi_check_system(dmi_blacklist); } @@ -1163,3 +1113,52 @@ void __init dmi_scan_machine(void) } EXPORT_SYMBOL(is_unsafe_smbus); + + +/** + * dmi_check_system - check system DMI data + * @list: array of dmi_system_id structures to match against + * + * Walk the blacklist table running matching functions until someone + * returns non zero or we hit the end. Callback function is called for + * each successfull match. Returns the number of matches. + */ +int dmi_check_system(struct dmi_system_id *list) +{ + int i, count = 0; + struct dmi_system_id *d = list; + + while (d->ident) { + for (i = 0; i < ARRAY_SIZE(d->matches); i++) { + int s = d->matches[i].slot; + if (s == DMI_NONE) + continue; + if (dmi_ident[s] && strstr(dmi_ident[s], d->matches[i].substr)) + continue; + /* No match */ + goto fail; + } + if (d->callback && d->callback(d)) + break; + count++; +fail: d++; + } + + return count; +} + +EXPORT_SYMBOL(dmi_check_system); + +/** + * dmi_get_system_info - return DMI data value + * @field: data index (see enum dmi_filed) + * + * Returns one DMI data value, can be used to perform + * complex DMI data checks. + */ +char * dmi_get_system_info(int field) +{ + return dmi_ident[field]; +} + +EXPORT_SYMBOL(dmi_get_system_info); diff --git a/include/linux/dmi.h b/include/linux/dmi.h new file mode 100644 index 000000000000..d2bcf556088b --- /dev/null +++ b/include/linux/dmi.h @@ -0,0 +1,47 @@ +#ifndef __DMI_H__ +#define __DMI_H__ + +enum dmi_field { + DMI_NONE, + DMI_BIOS_VENDOR, + DMI_BIOS_VERSION, + DMI_BIOS_DATE, + DMI_SYS_VENDOR, + DMI_PRODUCT_NAME, + DMI_PRODUCT_VERSION, + DMI_BOARD_VENDOR, + DMI_BOARD_NAME, + DMI_BOARD_VERSION, + DMI_STRING_MAX, +}; + +/* + * DMI callbacks for problem boards + */ +struct dmi_strmatch { + u8 slot; + char *substr; +}; + +struct dmi_system_id { + int (*callback)(struct dmi_system_id *); + char *ident; + struct dmi_strmatch matches[4]; + void *driver_data; +}; + +#define DMI_MATCH(a,b) { a, b } + +#if defined(CONFIG_X86) && !defined(CONFIG_X86_64) + +extern int dmi_check_system(struct dmi_system_id *list); +extern char * dmi_get_system_info(int field); + +#else + +static inline int dmi_check_system(struct dmi_system_id *list) { return 0; } +static inline char * dmi_get_system_info(int field) { return NULL; } + +#endif + +#endif /* __DMI_H__ */ -- cgit v1.2.3 From 17e14befcea27d734fd4f2247f2186683ee35ae0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Sun, 20 Jun 2004 04:52:59 -0700 Subject: [PATCH] Permit inode & dentry hash tables to be allocated > MAX_ORDER size Here's a patch to allocate memory for big system hash tables with the bootmem allocator rather than with main page allocator. It is needed for three reasons: (1) So that the size can be bigger than MAX_ORDER. IBM have done some testing on their big PPC64 systems (64GB of RAM) with linux-2.4 and found that they get better performance if the sizes of the inode cache hash, dentry cache hash, buffer head hash and page cache hash are increased beyond MAX_ORDER (order 11). Now the main allocator can't allocate anything larger than MAX_ORDER, but the bootmem allocator can. In 2.6 it appears that only the inode and dentry hashes remain of those four, but there are other hash tables that could use this service. (2) Changing MAX_ORDER appears to have a number of effects beyond just limiting the maximum size that can be allocated in one go. (3) Should someone want a hash table in which each bucket isn't a power of two in size, memory will be wasted as the chunk of memory allocated will be a power of two in size (to hold a power of two number of buckets). On the other hand, using the bootmem allocator means the allocation will only take up sufficient pages to hold it, rather than the next power of two up. Admittedly, this point doesn't apply to the dentry and inode hashes, but it might to another hash table that might want to use this service. I've coelesced the meat of the inode and dentry allocation routines into one such routine in mm/page_alloc.c that the the respective initialisation functions now call before mem_init() is called. This routine gets it's approximation of memory size by counting up the ZONE_NORMAL and ZONE_DMA pages (and ZONE_HIGHMEM if requested) in all the nodes passed to the main allocator by paging_init() (or wherever the arch does it). It does not use max_low_pfn as that doesn't seem to be available on all archs, and it doesn't use num_physpages since that includes highmem pages not available to the kernel for allocating data structures upon - which may not be appropriate when calculating hash table size. On the off chance that the size of each hash bucket may not be exactly a power of two, the routine will only allocate as many pages as is necessary to ensure that the number of buckets is exactly a power of two, rather than allocating the smallest power-of-two sized chunk of memory that will hold the same array of buckets. The maximum size of any single hash table is given by MAX_SYS_HASH_TABLE_ORDER, as is now defined in linux/mmzone.h. Signed-off-by: Paul Mackerras Signed-off-by: David Howells Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/dcache.c | 68 ++++++++++++++++----------------------------- fs/inode.c | 60 ++++++++++++---------------------------- include/linux/bootmem.h | 8 ++++++ include/linux/fs.h | 11 +++++--- include/linux/kernel.h | 9 ++++++ include/linux/mmzone.h | 12 ++++++++ init/main.c | 1 + mm/page_alloc.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 152 insertions(+), 90 deletions(-) (limited to 'include') diff --git a/fs/dcache.c b/fs/dcache.c index 613ff66dacde..4c632e1261dc 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -30,6 +30,7 @@ #include #include #include +#include #define DCACHE_PARANOIA 1 /* #define DCACHE_DEBUG 1 */ @@ -1561,13 +1562,25 @@ static int __init set_dhash_entries(char *str) } __setup("dhash_entries=", set_dhash_entries); -static void __init dcache_init(unsigned long mempages) +static void __init dcache_init_early(void) { - struct hlist_head *d; - unsigned long order; - unsigned int nr_hash; - int i; + int loop; + + dentry_hashtable = + alloc_large_system_hash("Dentry cache", + sizeof(struct hlist_head), + dhash_entries, + 13, + 0, + &d_hash_shift, + &d_hash_mask); + + for (loop = 0; loop < (1 << d_hash_shift); loop++) + INIT_HLIST_HEAD(&dentry_hashtable[loop]); +} +static void __init dcache_init(unsigned long mempages) +{ /* * A constructor could be added for stable state like the lists, * but it is probably not worth it because of the cache nature @@ -1580,45 +1593,6 @@ static void __init dcache_init(unsigned long mempages) NULL, NULL); set_shrinker(DEFAULT_SEEKS, shrink_dcache_memory); - - if (!dhash_entries) - dhash_entries = PAGE_SHIFT < 13 ? - mempages >> (13 - PAGE_SHIFT) : - mempages << (PAGE_SHIFT - 13); - - dhash_entries *= sizeof(struct hlist_head); - for (order = 0; ((1UL << order) << PAGE_SHIFT) < dhash_entries; order++) - ; - - do { - unsigned long tmp; - - nr_hash = (1UL << order) * PAGE_SIZE / - sizeof(struct hlist_head); - d_hash_mask = (nr_hash - 1); - - tmp = nr_hash; - d_hash_shift = 0; - while ((tmp >>= 1UL) != 0UL) - d_hash_shift++; - - dentry_hashtable = (struct hlist_head *) - __get_free_pages(GFP_ATOMIC, order); - } while (dentry_hashtable == NULL && --order >= 0); - - printk(KERN_INFO "Dentry cache hash table entries: %d (order: %ld, %ld bytes)\n", - nr_hash, order, (PAGE_SIZE << order)); - - if (!dentry_hashtable) - panic("Failed to allocate dcache hash table\n"); - - d = dentry_hashtable; - i = nr_hash; - do { - INIT_HLIST_HEAD(d); - d++; - i--; - } while (i); } /* SLAB cache for __getname() consumers */ @@ -1632,6 +1606,12 @@ EXPORT_SYMBOL(d_genocide); extern void bdev_cache_init(void); extern void chrdev_init(void); +void __init vfs_caches_init_early(void) +{ + dcache_init_early(); + inode_init_early(); +} + void __init vfs_caches_init(unsigned long mempages) { unsigned long reserve; diff --git a/fs/inode.c b/fs/inode.c index 042e3bb454dd..e802a3c35fd2 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -20,6 +20,7 @@ #include #include #include +#include /* * This is needed for the following functions: @@ -1345,55 +1346,30 @@ __setup("ihash_entries=", set_ihash_entries); /* * Initialize the waitqueues and inode hash table. */ +void __init inode_init_early(void) +{ + int loop; + + inode_hashtable = + alloc_large_system_hash("Inode-cache", + sizeof(struct hlist_head), + ihash_entries, + 14, + 0, + &i_hash_shift, + &i_hash_mask); + + for (loop = 0; loop < (1 << i_hash_shift); loop++) + INIT_HLIST_HEAD(&inode_hashtable[loop]); +} + void __init inode_init(unsigned long mempages) { - struct hlist_head *head; - unsigned long order; - unsigned int nr_hash; int i; for (i = 0; i < ARRAY_SIZE(i_wait_queue_heads); i++) init_waitqueue_head(&i_wait_queue_heads[i].wqh); - if (!ihash_entries) - ihash_entries = PAGE_SHIFT < 14 ? - mempages >> (14 - PAGE_SHIFT) : - mempages << (PAGE_SHIFT - 14); - - ihash_entries *= sizeof(struct hlist_head); - for (order = 0; ((1UL << order) << PAGE_SHIFT) < ihash_entries; order++) - ; - - do { - unsigned long tmp; - - nr_hash = (1UL << order) * PAGE_SIZE / - sizeof(struct hlist_head); - i_hash_mask = (nr_hash - 1); - - tmp = nr_hash; - i_hash_shift = 0; - while ((tmp >>= 1UL) != 0UL) - i_hash_shift++; - - inode_hashtable = (struct hlist_head *) - __get_free_pages(GFP_ATOMIC, order); - } while (inode_hashtable == NULL && --order >= 0); - - printk("Inode-cache hash table entries: %d (order: %ld, %ld bytes)\n", - nr_hash, order, (PAGE_SIZE << order)); - - if (!inode_hashtable) - panic("Failed to allocate inode hash table\n"); - - head = inode_hashtable; - i = nr_hash; - do { - INIT_HLIST_HEAD(head); - head++; - i--; - } while (i); - /* inode slab cache */ inode_cachep = kmem_cache_create("inode_cache", sizeof(struct inode), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, init_once, diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index 6902724691d2..e038f9a3d0ef 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -67,4 +67,12 @@ extern void * __init __alloc_bootmem_node (pg_data_t *pgdat, unsigned long size, __alloc_bootmem_node((pgdat), (x), PAGE_SIZE, 0) #endif /* !CONFIG_HAVE_ARCH_BOOTMEM_NODE */ +extern void *__init alloc_large_system_hash(const char *tablename, + unsigned long bucketsize, + unsigned long numentries, + int scale, + int consider_highmem, + unsigned int *_hash_shift, + unsigned int *_hash_mask); + #endif /* _LINUX_BOOTMEM_H */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 10a69a544045..504ca447700d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -214,15 +214,17 @@ extern int leases_enable, dir_notify_enable, lease_break_time; #include #include #include +#include #include #include /* Used to be a macro which just called the function, now just a function */ extern void update_atime (struct inode *); -extern void inode_init(unsigned long); -extern void mnt_init(unsigned long); -extern void files_init(unsigned long); +extern void __init inode_init(unsigned long); +extern void __init inode_init_early(void); +extern void __init mnt_init(unsigned long); +extern void __init files_init(unsigned long); struct buffer_head; typedef int (get_block_t)(struct inode *inode, sector_t iblock, @@ -1199,7 +1201,8 @@ extern int filp_close(struct file *, fl_owner_t id); extern char * getname(const char __user *); /* fs/dcache.c */ -extern void vfs_caches_init(unsigned long); +extern void __init vfs_caches_init_early(void); +extern void __init vfs_caches_init(unsigned long); #define __getname() kmem_cache_alloc(names_cachep, SLAB_KERNEL) #define __putname(name) kmem_cache_free(names_cachep, (void *)(name)) diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 67da15c2e1e3..9eb023020b44 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -92,6 +92,15 @@ asmlinkage int printk(const char * fmt, ...) unsigned long int_sqrt(unsigned long); +static inline int __attribute_pure__ long_log2(unsigned long x) +{ + int r = 0; + for (x >>= 1; x > 0; x >>= 1) + r++; + return r; +} + + extern int printk_ratelimit(void); extern int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst); diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index f1e1a727acb2..373a13ba6f3f 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -20,6 +20,18 @@ #define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER #endif +/* + * system hash table size limits + * - on large memory machines, we may want to allocate a bigger hash than that + * permitted by MAX_ORDER, so we allocate with the bootmem allocator, and are + * limited to this size + */ +#if MAX_ORDER > 14 +#define MAX_SYS_HASH_TABLE_ORDER MAX_ORDER +#else +#define MAX_SYS_HASH_TABLE_ORDER 14 +#endif + struct free_area { struct list_head free_list; unsigned long *map; diff --git a/init/main.c b/init/main.c index 027896bba166..1e58b66b2144 100644 --- a/init/main.c +++ b/init/main.c @@ -458,6 +458,7 @@ asmlinkage void __init start_kernel(void) initrd_start = 0; } #endif + vfs_caches_init_early(); mem_init(); kmem_cache_init(); numa_policy_init(); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 444bb534dbd8..16d5c2af94ee 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -55,6 +55,9 @@ EXPORT_SYMBOL(zone_table); static char *zone_names[MAX_NR_ZONES] = { "DMA", "Normal", "HighMem" }; int min_free_kbytes = 1024; +static unsigned long __initdata nr_kernel_pages; +static unsigned long __initdata nr_all_pages; + /* * Temporary debugging check for pages not lying within a given zone. */ @@ -1430,6 +1433,10 @@ static void __init free_area_init_core(struct pglist_data *pgdat, if (zholes_size) realsize -= zholes_size[j]; + if (j == ZONE_DMA || j == ZONE_NORMAL) + nr_kernel_pages += realsize; + nr_all_pages += realsize; + zone->spanned_pages = size; zone->present_pages = realsize; zone->name = zone_names[j]; @@ -1970,3 +1977,69 @@ int lower_zone_protection_sysctl_handler(ctl_table *table, int write, setup_per_zone_protection(); return 0; } + +/* + * allocate a large system hash table from bootmem + * - it is assumed that the hash table must contain an exact power-of-2 + * quantity of entries + */ +void *__init alloc_large_system_hash(const char *tablename, + unsigned long bucketsize, + unsigned long numentries, + int scale, + int consider_highmem, + unsigned int *_hash_shift, + unsigned int *_hash_mask) +{ + unsigned long mem, max, log2qty, size; + void *table; + + /* round applicable memory size up to nearest megabyte */ + mem = consider_highmem ? nr_all_pages : nr_kernel_pages; + mem += (1UL << (20 - PAGE_SHIFT)) - 1; + mem >>= 20 - PAGE_SHIFT; + mem <<= 20 - PAGE_SHIFT; + + /* limit to 1 bucket per 2^scale bytes of low memory (rounded up to + * nearest power of 2 in size) */ + if (scale > PAGE_SHIFT) + mem >>= (scale - PAGE_SHIFT); + else + mem <<= (PAGE_SHIFT - scale); + + mem = 1UL << (long_log2(mem) + 1); + + /* limit allocation size */ + max = (1UL << (PAGE_SHIFT + MAX_SYS_HASH_TABLE_ORDER)) / bucketsize; + if (max > mem) + max = mem; + + /* allow the kernel cmdline to have a say */ + if (!numentries || numentries > max) + numentries = max; + + log2qty = long_log2(numentries); + + do { + size = bucketsize << log2qty; + + table = (void *) alloc_bootmem(size); + + } while (!table && size > PAGE_SIZE); + + if (!table) + panic("Failed to allocate %s hash table\n", tablename); + + printk("%s hash table entries: %d (order: %d, %lu bytes)\n", + tablename, + (1U << log2qty), + long_log2(size) - PAGE_SHIFT, + size); + + if (_hash_shift) + *_hash_shift = log2qty; + if (_hash_mask) + *_hash_mask = (1 << log2qty) - 1; + + return table; +} -- cgit v1.2.3 From 5a86174c17ab9d62fb7aced585cc5b6cb552411c Mon Sep 17 00:00:00 2001 From: Cesar Eduardo Barros Date: Sun, 20 Jun 2004 04:53:11 -0700 Subject: [PATCH] O_NOATIME support This patch adds support for the O_NOATIME open flag (GNU extension): int O_NOATIME Macro If this bit is set, read will not update the access time of the file. See File Times. This is used by programs that do backups, so that backing a file up does not count as reading it. Only the owner of the file or the superuser may use this bit. It is useful if you want to do something with the file atime (for instance, moving files that have not been accessed in a while to somewhere else, or something like Debian's popularity-contest) but you also want to read all files periodically (for instance, tripwire or debsums). Currently, the program that reads all files periodically has to use utimes, which can race with the atime update: A B open fstat read open read close close utimes And the file still has the old atime, instead of the new one from when B did the read from it. This problem does not happen if A uses O_NOATIME instead of utimes to preserve the atime. This patch adds the O_NOATIME constant for all architectures, but it would also be possible to add it one architecture at a time by defining it to 0 when not defined in asm-*. Based on patch by Marek Michalkiewicz at http://www.uwsg.iu.edu/hypermail/linux/kernel/9811.2/0118.html Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- fs/fcntl.c | 7 ++++++- fs/namei.c | 5 +++++ include/asm-alpha/fcntl.h | 1 + include/asm-arm/fcntl.h | 1 + include/asm-arm26/fcntl.h | 1 + include/asm-cris/fcntl.h | 1 + include/asm-h8300/fcntl.h | 1 + include/asm-i386/fcntl.h | 1 + include/asm-ia64/fcntl.h | 1 + include/asm-m68k/fcntl.h | 1 + include/asm-mips/fcntl.h | 1 + include/asm-parisc/fcntl.h | 1 + include/asm-ppc/fcntl.h | 1 + include/asm-ppc64/fcntl.h | 1 + include/asm-s390/fcntl.h | 1 + include/asm-sh/fcntl.h | 1 + include/asm-sparc/fcntl.h | 1 + include/asm-sparc64/fcntl.h | 1 + include/asm-v850/fcntl.h | 1 + include/asm-x86_64/fcntl.h | 1 + include/linux/fs.h | 3 ++- 21 files changed, 31 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/fcntl.c b/fs/fcntl.c index 13d351cba2e3..77cec9debe17 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -212,7 +212,7 @@ asmlinkage long sys_dup(unsigned int fildes) return ret; } -#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC | O_DIRECT) +#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC | O_DIRECT | O_NOATIME) static int setfl(int fd, struct file * filp, unsigned long arg) { @@ -223,6 +223,11 @@ static int setfl(int fd, struct file * filp, unsigned long arg) if (!(arg & O_APPEND) && IS_APPEND(inode)) return -EPERM; + /* O_NOATIME can only be set by the owner or superuser */ + if ((arg & O_NOATIME) && !(filp->f_flags & O_NOATIME)) + if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER)) + return -EPERM; + /* required for strict SunOS emulation */ if (O_NONBLOCK != O_NDELAY) if (arg & O_NDELAY) diff --git a/fs/namei.c b/fs/namei.c index 9828b94fe8bb..efaaf1dd1d7d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1206,6 +1206,11 @@ int may_open(struct nameidata *nd, int acc_mode, int flag) return -EPERM; } + /* O_NOATIME can only be set by the owner or superuser */ + if (flag & O_NOATIME) + if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER)) + return -EPERM; + /* * Ensure there are no outstanding leases on the file. */ diff --git a/include/asm-alpha/fcntl.h b/include/asm-alpha/fcntl.h index ad40d002f006..6b7d6c1649ce 100644 --- a/include/asm-alpha/fcntl.h +++ b/include/asm-alpha/fcntl.h @@ -21,6 +21,7 @@ #define O_NOFOLLOW 0200000 /* don't follow links */ #define O_LARGEFILE 0400000 /* will be set by the kernel on every open */ #define O_DIRECT 02000000 /* direct disk access - should check with OSF/1 */ +#define O_NOATIME 04000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/asm-arm/fcntl.h b/include/asm-arm/fcntl.h index da2861ca37b0..485b6bdf4d7a 100644 --- a/include/asm-arm/fcntl.h +++ b/include/asm-arm/fcntl.h @@ -20,6 +20,7 @@ #define O_NOFOLLOW 0100000 /* don't follow links */ #define O_DIRECT 0200000 /* direct disk access hint - currently ignored */ #define O_LARGEFILE 0400000 +#define O_NOATIME 01000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/asm-arm26/fcntl.h b/include/asm-arm26/fcntl.h index da2861ca37b0..485b6bdf4d7a 100644 --- a/include/asm-arm26/fcntl.h +++ b/include/asm-arm26/fcntl.h @@ -20,6 +20,7 @@ #define O_NOFOLLOW 0100000 /* don't follow links */ #define O_DIRECT 0200000 /* direct disk access hint - currently ignored */ #define O_LARGEFILE 0400000 +#define O_NOATIME 01000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/asm-cris/fcntl.h b/include/asm-cris/fcntl.h index a68e2886e0c1..61c563242b51 100644 --- a/include/asm-cris/fcntl.h +++ b/include/asm-cris/fcntl.h @@ -22,6 +22,7 @@ #define O_LARGEFILE 0100000 #define O_DIRECTORY 0200000 /* must be a directory */ #define O_NOFOLLOW 0400000 /* don't follow links */ +#define O_NOATIME 01000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get f_flags */ diff --git a/include/asm-h8300/fcntl.h b/include/asm-h8300/fcntl.h index a7e7ac01d0d7..355350a57bf9 100644 --- a/include/asm-h8300/fcntl.h +++ b/include/asm-h8300/fcntl.h @@ -20,6 +20,7 @@ #define O_NOFOLLOW 0100000 /* don't follow links */ #define O_DIRECT 0200000 /* direct disk access hint - currently ignored */ #define O_LARGEFILE 0400000 +#define O_NOATIME 01000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/asm-i386/fcntl.h b/include/asm-i386/fcntl.h index 41e3c4d9144e..511cde94a3ed 100644 --- a/include/asm-i386/fcntl.h +++ b/include/asm-i386/fcntl.h @@ -20,6 +20,7 @@ #define O_LARGEFILE 0100000 #define O_DIRECTORY 0200000 /* must be a directory */ #define O_NOFOLLOW 0400000 /* don't follow links */ +#define O_NOATIME 01000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/asm-ia64/fcntl.h b/include/asm-ia64/fcntl.h index 697dd0bc0a8e..d193981bb1d8 100644 --- a/include/asm-ia64/fcntl.h +++ b/include/asm-ia64/fcntl.h @@ -28,6 +28,7 @@ #define O_LARGEFILE 0100000 #define O_DIRECTORY 0200000 /* must be a directory */ #define O_NOFOLLOW 0400000 /* don't follow links */ +#define O_NOATIME 01000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/asm-m68k/fcntl.h b/include/asm-m68k/fcntl.h index c0b273f68f05..0d4212983a33 100644 --- a/include/asm-m68k/fcntl.h +++ b/include/asm-m68k/fcntl.h @@ -20,6 +20,7 @@ #define O_NOFOLLOW 0100000 /* don't follow links */ #define O_DIRECT 0200000 /* direct disk access hint - currently ignored */ #define O_LARGEFILE 0400000 +#define O_NOATIME 01000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/asm-mips/fcntl.h b/include/asm-mips/fcntl.h index e2b9c0a2537b..2436392e7990 100644 --- a/include/asm-mips/fcntl.h +++ b/include/asm-mips/fcntl.h @@ -26,6 +26,7 @@ #define O_DIRECT 0x8000 /* direct disk access hint */ #define O_DIRECTORY 0x10000 /* must be a directory */ #define O_NOFOLLOW 0x20000 /* don't follow links */ +#define O_NOATIME 0x40000 #define O_NDELAY O_NONBLOCK diff --git a/include/asm-parisc/fcntl.h b/include/asm-parisc/fcntl.h index 01fe48b0ba9d..def35230716a 100644 --- a/include/asm-parisc/fcntl.h +++ b/include/asm-parisc/fcntl.h @@ -19,6 +19,7 @@ #define O_NOCTTY 00400000 /* not fcntl */ #define O_DSYNC 01000000 /* HPUX only */ #define O_RSYNC 02000000 /* HPUX only */ +#define O_NOATIME 04000000 #define FASYNC 00020000 /* fcntl, for BSD compatibility */ #define O_DIRECT 00040000 /* direct disk access hint - currently ignored */ diff --git a/include/asm-ppc/fcntl.h b/include/asm-ppc/fcntl.h index 27ae7d2f6af2..5e28e41fb29f 100644 --- a/include/asm-ppc/fcntl.h +++ b/include/asm-ppc/fcntl.h @@ -20,6 +20,7 @@ #define O_NOFOLLOW 0100000 /* don't follow links */ #define O_LARGEFILE 0200000 #define O_DIRECT 0400000 /* direct disk access hint */ +#define O_NOATIME 01000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/asm-ppc64/fcntl.h b/include/asm-ppc64/fcntl.h index 1ef83570b4e4..842560d50656 100644 --- a/include/asm-ppc64/fcntl.h +++ b/include/asm-ppc64/fcntl.h @@ -27,6 +27,7 @@ #define O_NOFOLLOW 0100000 /* don't follow links */ #define O_LARGEFILE 0200000 #define O_DIRECT 0400000 /* direct disk access hint */ +#define O_NOATIME 01000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/asm-s390/fcntl.h b/include/asm-s390/fcntl.h index 307c01b45213..48f692b45732 100644 --- a/include/asm-s390/fcntl.h +++ b/include/asm-s390/fcntl.h @@ -27,6 +27,7 @@ #define O_LARGEFILE 0100000 #define O_DIRECTORY 0200000 /* must be a directory */ #define O_NOFOLLOW 0400000 /* don't follow links */ +#define O_NOATIME 01000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/asm-sh/fcntl.h b/include/asm-sh/fcntl.h index 21e39b7cc05a..0b3ae524e34c 100644 --- a/include/asm-sh/fcntl.h +++ b/include/asm-sh/fcntl.h @@ -20,6 +20,7 @@ #define O_LARGEFILE 0100000 #define O_DIRECTORY 0200000 /* must be a directory */ #define O_NOFOLLOW 0400000 /* don't follow links */ +#define O_NOATIME 01000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/asm-sparc/fcntl.h b/include/asm-sparc/fcntl.h index aa64cfc1df2e..df9c75d41d68 100644 --- a/include/asm-sparc/fcntl.h +++ b/include/asm-sparc/fcntl.h @@ -21,6 +21,7 @@ #define O_NOFOLLOW 0x20000 /* don't follow links */ #define O_LARGEFILE 0x40000 #define O_DIRECT 0x100000 /* direct disk access hint */ +#define O_NOATIME 0x200000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/asm-sparc64/fcntl.h b/include/asm-sparc64/fcntl.h index 0999d6d525c1..e36def0d0d80 100644 --- a/include/asm-sparc64/fcntl.h +++ b/include/asm-sparc64/fcntl.h @@ -21,6 +21,7 @@ #define O_NOFOLLOW 0x20000 /* don't follow links */ #define O_LARGEFILE 0x40000 #define O_DIRECT 0x100000 /* direct disk access hint */ +#define O_NOATIME 0x200000 #define F_DUPFD 0 /* dup */ diff --git a/include/asm-v850/fcntl.h b/include/asm-v850/fcntl.h index 42e358ff0752..31d4b5961221 100644 --- a/include/asm-v850/fcntl.h +++ b/include/asm-v850/fcntl.h @@ -20,6 +20,7 @@ #define O_NOFOLLOW 0100000 /* don't follow links */ #define O_DIRECT 0200000 /* direct disk access hint - currently ignored */ #define O_LARGEFILE 0400000 +#define O_NOATIME 01000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/asm-x86_64/fcntl.h b/include/asm-x86_64/fcntl.h index aabf1a30a3b9..4411f221c037 100644 --- a/include/asm-x86_64/fcntl.h +++ b/include/asm-x86_64/fcntl.h @@ -20,6 +20,7 @@ #define O_LARGEFILE 0100000 #define O_DIRECTORY 0200000 /* must be a directory */ #define O_NOFOLLOW 0400000 /* don't follow links */ +#define O_NOATIME 01000000 #define F_DUPFD 0 /* dup */ #define F_GETFD 1 /* get close_on_exec */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 504ca447700d..88337ed4f4f2 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -979,7 +979,8 @@ static inline void touch_atime(struct vfsmount *mnt, struct dentry *dentry) static inline void file_accessed(struct file *file) { - touch_atime(file->f_vfsmnt, file->f_dentry); + if (!(file->f_flags & O_NOATIME)) + touch_atime(file->f_vfsmnt, file->f_dentry); } int sync_inode(struct inode *inode, struct writeback_control *wbc); -- cgit v1.2.3 From 9b3b3c3b19e91595858726616e59982f07b5d677 Mon Sep 17 00:00:00 2001 From: "Mark W. McClelland" Date: Sun, 20 Jun 2004 04:54:07 -0700 Subject: [PATCH] Add ovcamchip driver This patch adds a new driver for the OmniVision OV6xx0 and OV7xx0 series of CMOS image sensors. It is currently used by the w9968cf USB webcam driver, which is already in mainline 2.6. Up until now it had to be compiled outside the kernel tree, which is clearly suboptimal. It is also used by version 2 of the ov511 USB webcam driver, which will be merged in the near future. That will reduce some code duplication, since the existing ov511 has much of this code built-in. This was previously submitted to Linux-USB-Devel, and I have fixed the concerns that came up at that time. Developer's Certificate of Origin 1.0 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. Signed-off-by: Mark McClelland Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/media/video/Kconfig | 11 + drivers/media/video/Makefile | 1 + drivers/media/video/ovcamchip/Makefile | 4 + drivers/media/video/ovcamchip/ov6x20.c | 415 ++++++++++++++++++++++ drivers/media/video/ovcamchip/ov6x30.c | 374 ++++++++++++++++++++ drivers/media/video/ovcamchip/ov76be.c | 303 ++++++++++++++++ drivers/media/video/ovcamchip/ov7x10.c | 335 ++++++++++++++++++ drivers/media/video/ovcamchip/ov7x20.c | 455 +++++++++++++++++++++++++ drivers/media/video/ovcamchip/ovcamchip_core.c | 446 ++++++++++++++++++++++++ drivers/media/video/ovcamchip/ovcamchip_priv.h | 87 +++++ include/media/ovcamchip.h | 104 ++++++ 11 files changed, 2535 insertions(+) create mode 100644 drivers/media/video/ovcamchip/Makefile create mode 100644 drivers/media/video/ovcamchip/ov6x20.c create mode 100644 drivers/media/video/ovcamchip/ov6x30.c create mode 100644 drivers/media/video/ovcamchip/ov76be.c create mode 100644 drivers/media/video/ovcamchip/ov7x10.c create mode 100644 drivers/media/video/ovcamchip/ov7x20.c create mode 100644 drivers/media/video/ovcamchip/ovcamchip_core.c create mode 100644 drivers/media/video/ovcamchip/ovcamchip_priv.h create mode 100644 include/media/ovcamchip.h (limited to 'include') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 90249af1efca..5c484f27a475 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -295,5 +295,16 @@ config VIDEO_CX88 To compile this driver as a module, choose M here: the module will be called cx8800 +config VIDEO_OVCAMCHIP + tristate "OmniVision Camera Chip support" + depends on VIDEO_DEV && I2C + ---help--- + Support for the OmniVision OV6xxx and OV7xxx series of camera chips. + This driver is intended to be used with the ov511 and w9968cf USB + camera drivers. + + To compile this driver as a module, choose M here: the + module will be called ovcamchip + endmenu diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 56f2e7c4a3f4..6a14ce96f190 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -37,6 +37,7 @@ obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ +obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/ obj-$(CONFIG_VIDEO_MXB) += saa7111.o tuner.o tda9840.o tea6415c.o tea6420.o mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o diff --git a/drivers/media/video/ovcamchip/Makefile b/drivers/media/video/ovcamchip/Makefile new file mode 100644 index 000000000000..bca41ad93de8 --- /dev/null +++ b/drivers/media/video/ovcamchip/Makefile @@ -0,0 +1,4 @@ +ovcamchip-objs := ovcamchip_core.o ov6x20.o ov6x30.o ov7x10.o ov7x20.o \ + ov76be.o + +obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip.o diff --git a/drivers/media/video/ovcamchip/ov6x20.c b/drivers/media/video/ovcamchip/ov6x20.c new file mode 100644 index 000000000000..3433619ad93f --- /dev/null +++ b/drivers/media/video/ovcamchip/ov6x20.c @@ -0,0 +1,415 @@ +/* OmniVision OV6620/OV6120 Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * 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. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* Registers */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_SAT 0x03 /* saturation */ +#define REG_CNT 0x05 /* Y contrast */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_WB_BLUE 0x0C /* WB blue ratio [5:0] */ +#define REG_WB_RED 0x0D /* WB red ratio [5:0] */ +#define REG_EXP 0x10 /* exposure */ + +/* Window parameters */ +#define HWSBASE 0x38 +#define HWEBASE 0x3A +#define VWSBASE 0x05 +#define VWEBASE 0x06 + +struct ov6x20 { + int auto_brt; + int auto_exp; + int backlight; + int bandfilt; + int mirror; +}; + +/* Initial values for use with OV511/OV511+ cameras */ +static struct ovcamchip_regvals regvals_init_6x20_511[] = { + { 0x12, 0x80 }, /* reset */ + { 0x11, 0x01 }, + { 0x03, 0x60 }, + { 0x05, 0x7f }, /* For when autoadjust is off */ + { 0x07, 0xa8 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x0f, 0x15 }, /* COMS */ + { 0x10, 0x75 }, /* AEC Exposure time */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x14, 0x04 }, + { 0x16, 0x03 }, + { 0x26, 0xb2 }, /* BLC enable */ + /* 0x28: 0x05 Selects RGB format if RGB on */ + { 0x28, 0x05 }, + { 0x2a, 0x04 }, /* Disable framerate adjust */ + { 0x2d, 0x99 }, + { 0x33, 0xa0 }, /* Color Processing Parameter */ + { 0x34, 0xd2 }, /* Max A/D range */ + { 0x38, 0x8b }, + { 0x39, 0x40 }, + + { 0x3c, 0x39 }, /* Enable AEC mode changing */ + { 0x3c, 0x3c }, /* Change AEC mode */ + { 0x3c, 0x24 }, /* Disable AEC mode changing */ + + { 0x3d, 0x80 }, + /* These next two registers (0x4a, 0x4b) are undocumented. They + * control the color balance */ + { 0x4a, 0x80 }, + { 0x4b, 0x80 }, + { 0x4d, 0xd2 }, /* This reduces noise a bit */ + { 0x4e, 0xc1 }, + { 0x4f, 0x04 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* Initial values for use with OV518 cameras */ +static struct ovcamchip_regvals regvals_init_6x20_518[] = { + { 0x12, 0x80 }, /* Do a reset */ + { 0x03, 0xc0 }, /* Saturation */ + { 0x05, 0x8a }, /* Contrast */ + { 0x0c, 0x24 }, /* AWB blue */ + { 0x0d, 0x24 }, /* AWB red */ + { 0x0e, 0x8d }, /* Additional 2x gain */ + { 0x0f, 0x25 }, /* Black expanding level = 1.3V */ + { 0x11, 0x01 }, /* Clock div. */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x13, 0x01 }, /* (default) */ + { 0x14, 0x80 }, /* Set reserved bit 7 */ + { 0x15, 0x01 }, /* (default) */ + { 0x16, 0x03 }, /* (default) */ + { 0x17, 0x38 }, /* (default) */ + { 0x18, 0xea }, /* (default) */ + { 0x19, 0x04 }, + { 0x1a, 0x93 }, + { 0x1b, 0x00 }, /* (default) */ + { 0x1e, 0xc4 }, /* (default) */ + { 0x1f, 0x04 }, /* (default) */ + { 0x20, 0x20 }, /* Enable 1st stage aperture correction */ + { 0x21, 0x10 }, /* Y offset */ + { 0x22, 0x88 }, /* U offset */ + { 0x23, 0xc0 }, /* Set XTAL power level */ + { 0x24, 0x53 }, /* AEC bright ratio */ + { 0x25, 0x7a }, /* AEC black ratio */ + { 0x26, 0xb2 }, /* BLC enable */ + { 0x27, 0xa2 }, /* Full output range */ + { 0x28, 0x01 }, /* (default) */ + { 0x29, 0x00 }, /* (default) */ + { 0x2a, 0x84 }, /* (default) */ + { 0x2b, 0xa8 }, /* Set custom frame rate */ + { 0x2c, 0xa0 }, /* (reserved) */ + { 0x2d, 0x95 }, /* Enable banding filter */ + { 0x2e, 0x88 }, /* V offset */ + { 0x33, 0x22 }, /* Luminance gamma on */ + { 0x34, 0xc7 }, /* A/D bias */ + { 0x36, 0x12 }, /* (reserved) */ + { 0x37, 0x63 }, /* (reserved) */ + { 0x38, 0x8b }, /* Quick AEC/AEB */ + { 0x39, 0x00 }, /* (default) */ + { 0x3a, 0x0f }, /* (default) */ + { 0x3b, 0x3c }, /* (default) */ + { 0x3c, 0x5c }, /* AEC controls */ + { 0x3d, 0x80 }, /* Drop 1 (bad) frame when AEC change */ + { 0x3e, 0x80 }, /* (default) */ + { 0x3f, 0x02 }, /* (default) */ + { 0x40, 0x10 }, /* (reserved) */ + { 0x41, 0x10 }, /* (reserved) */ + { 0x42, 0x00 }, /* (reserved) */ + { 0x43, 0x7f }, /* (reserved) */ + { 0x44, 0x80 }, /* (reserved) */ + { 0x45, 0x1c }, /* (reserved) */ + { 0x46, 0x1c }, /* (reserved) */ + { 0x47, 0x80 }, /* (reserved) */ + { 0x48, 0x5f }, /* (reserved) */ + { 0x49, 0x00 }, /* (reserved) */ + { 0x4a, 0x00 }, /* Color balance (undocumented) */ + { 0x4b, 0x80 }, /* Color balance (undocumented) */ + { 0x4c, 0x58 }, /* (reserved) */ + { 0x4d, 0xd2 }, /* U *= .938, V *= .838 */ + { 0x4e, 0xa0 }, /* (default) */ + { 0x4f, 0x04 }, /* UV 3-point average */ + { 0x50, 0xff }, /* (reserved) */ + { 0x51, 0x58 }, /* (reserved) */ + { 0x52, 0xc0 }, /* (reserved) */ + { 0x53, 0x42 }, /* (reserved) */ + { 0x27, 0xa6 }, /* Enable manual offset adj. (reg 21 & 22) */ + { 0x12, 0x20 }, + { 0x12, 0x24 }, + + { 0xff, 0xff }, /* END MARKER */ +}; + +/* This initializes the OV6x20 camera chip and relevant variables. */ +static int ov6x20_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x20 *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + switch (c->adapter->id) { + case I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV511: + rc = ov_write_regvals(c, regvals_init_6x20_511); + break; + case I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV518: + rc = ov_write_regvals(c, regvals_init_6x20_518); + break; + default: + dev_err(&c->dev, "ov6x20: Unsupported adapter\n"); + rc = -ENODEV; + } + + if (rc < 0) + return rc; + + ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + memset(s, 0, sizeof *s); + + s->auto_brt = 1; + s->auto_exp = 1; + + return rc; +} + +static int ov6x20_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov6x20_set_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x20 *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_write(c, REG_CNT, v >> 8); + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_write(c, REG_BRT, v >> 8); + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_HUE: + rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); + if (rc < 0) + goto out; + + rc = ov_write(c, REG_BLUE, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + rc = ov_write(c, REG_EXP, v); + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write(c, 0x2b, sixty?0xa8:0x28); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2a, sixty?0x84:0xa4); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_BACKLIGHT: + { + rc = ov_write_mask(c, 0x4e, v?0xe0:0xc0, 0xe0); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x0e, v?0x80:0x00, 0x80); + s->backlight = v; + break; + } + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov6x20_get_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x20 *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_read(c, REG_CNT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_HUE: + rc = ov_read(c, REG_BLUE, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_BACKLIGHT: + ctl->value = s->backlight; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov6x20_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + /******** QCIF-specific regs ********/ + + ov_write(c, 0x14, win->quarter?0x24:0x04); + + /******** Palette-specific regs ********/ + + /* OV518 needs 8 bit multiplexed in color mode, and 16 bit in B&W */ + if (c->adapter->id == (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV518)) { + if (win->format == VIDEO_PALETTE_GREY) + ov_write_mask(c, 0x13, 0x00, 0x20); + else + ov_write_mask(c, 0x13, 0x20, 0x20); + } else { + if (win->format == VIDEO_PALETTE_GREY) + ov_write_mask(c, 0x13, 0x20, 0x20); + else + ov_write_mask(c, 0x13, 0x00, 0x20); + } + + /******** Clock programming ********/ + + /* The OV6620 needs special handling. This prevents the + * severe banding that normally occurs */ + + /* Clock down */ + ov_write(c, 0x2a, 0x04); + + ov_write(c, 0x11, win->clockdiv); + + ov_write(c, 0x2a, 0x84); + /* This next setting is critical. It seems to improve + * the gain or the contrast. The "reserved" bits seem + * to have some effect in this case. */ + ov_write(c, 0x2d, 0x85); /* FIXME: This messes up banding filter */ + + return 0; +} + +static int ov6x20_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov6x20_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 0; + vwscale = 0; + } else { + hwscale = 1; + vwscale = 1; /* The datasheet says 0; it's wrong */ + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov6x20_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov6x20_set_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov6x20_get_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov6x20_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov6x20_ops = { + .init = ov6x20_init, + .free = ov6x20_free, + .command = ov6x20_command, +}; diff --git a/drivers/media/video/ovcamchip/ov6x30.c b/drivers/media/video/ovcamchip/ov6x30.c new file mode 100644 index 000000000000..44a842379b45 --- /dev/null +++ b/drivers/media/video/ovcamchip/ov6x30.c @@ -0,0 +1,374 @@ +/* OmniVision OV6630/OV6130 Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * 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. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* Registers */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_SAT 0x03 /* saturation [7:3] */ +#define REG_CNT 0x05 /* Y contrast [3:0] */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_SHARP 0x07 /* sharpness */ +#define REG_WB_BLUE 0x0C /* WB blue ratio [5:0] */ +#define REG_WB_RED 0x0D /* WB red ratio [5:0] */ +#define REG_EXP 0x10 /* exposure */ + +/* Window parameters */ +#define HWSBASE 0x38 +#define HWEBASE 0x3A +#define VWSBASE 0x05 +#define VWEBASE 0x06 + +struct ov6x30 { + int auto_brt; + int auto_exp; + int backlight; + int bandfilt; + int mirror; +}; + +static struct ovcamchip_regvals regvals_init_6x30[] = { + { 0x12, 0x80 }, /* reset */ + { 0x00, 0x1f }, /* Gain */ + { 0x01, 0x99 }, /* Blue gain */ + { 0x02, 0x7c }, /* Red gain */ + { 0x03, 0xc0 }, /* Saturation */ + { 0x05, 0x0a }, /* Contrast */ + { 0x06, 0x95 }, /* Brightness */ + { 0x07, 0x2d }, /* Sharpness */ + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x0e, 0x20 }, + { 0x0f, 0x05 }, + { 0x10, 0x9a }, /* "exposure check" */ + { 0x11, 0x00 }, /* Pixel clock = fastest */ + { 0x12, 0x24 }, /* Enable AGC and AWB */ + { 0x13, 0x21 }, + { 0x14, 0x80 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x38 }, + { 0x18, 0xea }, + { 0x19, 0x04 }, + { 0x1a, 0x93 }, + { 0x1b, 0x00 }, + { 0x1e, 0xc4 }, + { 0x1f, 0x04 }, + { 0x20, 0x20 }, + { 0x21, 0x10 }, + { 0x22, 0x88 }, + { 0x23, 0xc0 }, /* Crystal circuit power level */ + { 0x25, 0x9a }, /* Increase AEC black pixel ratio */ + { 0x26, 0xb2 }, /* BLC enable */ + { 0x27, 0xa2 }, + { 0x28, 0x00 }, + { 0x29, 0x00 }, + { 0x2a, 0x84 }, /* (keep) */ + { 0x2b, 0xa8 }, /* (keep) */ + { 0x2c, 0xa0 }, + { 0x2d, 0x95 }, /* Enable auto-brightness */ + { 0x2e, 0x88 }, + { 0x33, 0x26 }, + { 0x34, 0x03 }, + { 0x36, 0x8f }, + { 0x37, 0x80 }, + { 0x38, 0x83 }, + { 0x39, 0x80 }, + { 0x3a, 0x0f }, + { 0x3b, 0x3c }, + { 0x3c, 0x1a }, + { 0x3d, 0x80 }, + { 0x3e, 0x80 }, + { 0x3f, 0x0e }, + { 0x40, 0x00 }, /* White bal */ + { 0x41, 0x00 }, /* White bal */ + { 0x42, 0x80 }, + { 0x43, 0x3f }, /* White bal */ + { 0x44, 0x80 }, + { 0x45, 0x20 }, + { 0x46, 0x20 }, + { 0x47, 0x80 }, + { 0x48, 0x7f }, + { 0x49, 0x00 }, + { 0x4a, 0x00 }, + { 0x4b, 0x80 }, + { 0x4c, 0xd0 }, + { 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */ + { 0x4e, 0x40 }, + { 0x4f, 0x07 }, /* UV average mode, color killer: strongest */ + { 0x50, 0xff }, + { 0x54, 0x23 }, /* Max AGC gain: 18dB */ + { 0x55, 0xff }, + { 0x56, 0x12 }, + { 0x57, 0x81 }, /* (default) */ + { 0x58, 0x75 }, + { 0x59, 0x01 }, /* AGC dark current compensation: +1 */ + { 0x5a, 0x2c }, + { 0x5b, 0x0f }, /* AWB chrominance levels */ + { 0x5c, 0x10 }, + { 0x3d, 0x80 }, + { 0x27, 0xa6 }, + /* Toggle AWB off and on */ + { 0x12, 0x20 }, + { 0x12, 0x24 }, + + { 0xff, 0xff }, /* END MARKER */ +}; + +/* This initializes the OV6x30 camera chip and relevant variables. */ +static int ov6x30_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x30 *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + rc = ov_write_regvals(c, regvals_init_6x30); + if (rc < 0) + return rc; + + ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + memset(s, 0, sizeof *s); + + s->auto_brt = 1; + s->auto_exp = 1; + + return rc; +} + +static int ov6x30_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov6x30_set_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x30 *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_write_mask(c, REG_CNT, v >> 12, 0x0f); + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_write(c, REG_BRT, v >> 8); + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_HUE: + rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); + if (rc < 0) + goto out; + + rc = ov_write(c, REG_BLUE, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + rc = ov_write(c, REG_EXP, v); + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write(c, 0x2b, sixty?0xa8:0x28); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2a, sixty?0x84:0xa4); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x28, v?0x00:0x10, 0x10); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_BACKLIGHT: + { + rc = ov_write_mask(c, 0x4e, v?0x80:0x60, 0xe0); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x28, v?0x02:0x00, 0x02); + s->backlight = v; + break; + } + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov6x30_get_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov6x30 *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_read(c, REG_CNT, &val); + ctl->value = (val & 0x0f) << 12; + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_HUE: + rc = ov_read(c, REG_BLUE, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_BACKLIGHT: + ctl->value = s->backlight; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov6x30_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + /******** QCIF-specific regs ********/ + + ov_write_mask(c, 0x14, win->quarter?0x20:0x00, 0x20); + + /******** Palette-specific regs ********/ + + if (win->format == VIDEO_PALETTE_GREY) { + if (c->adapter->id == (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV518)) { + /* Do nothing - we're already in 8-bit mode */ + } else { + ov_write_mask(c, 0x13, 0x20, 0x20); + } + } else { + /* The OV518 needs special treatment. Although both the OV518 + * and the OV6630 support a 16-bit video bus, only the 8 bit Y + * bus is actually used. The UV bus is tied to ground. + * Therefore, the OV6630 needs to be in 8-bit multiplexed + * output mode */ + + if (c->adapter->id == (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV518)) { + /* Do nothing - we want to stay in 8-bit mode */ + /* Warning: Messing with reg 0x13 breaks OV518 color */ + } else { + ov_write_mask(c, 0x13, 0x00, 0x20); + } + } + + /******** Clock programming ********/ + + ov_write(c, 0x11, win->clockdiv); + + return 0; +} + +static int ov6x30_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov6x30_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 0; + vwscale = 0; + } else { + hwscale = 1; + vwscale = 1; /* The datasheet says 0; it's wrong */ + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov6x30_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov6x30_set_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov6x30_get_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov6x30_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov6x30_ops = { + .init = ov6x30_init, + .free = ov6x30_free, + .command = ov6x30_command, +}; diff --git a/drivers/media/video/ovcamchip/ov76be.c b/drivers/media/video/ovcamchip/ov76be.c new file mode 100644 index 000000000000..29bbdc05e3b6 --- /dev/null +++ b/drivers/media/video/ovcamchip/ov76be.c @@ -0,0 +1,303 @@ +/* OmniVision OV76BE Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * 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. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* OV7610 registers: Since the OV76BE is undocumented, we'll settle for these + * for now. */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue channel balance */ +#define REG_RED 0x02 /* red channel balance */ +#define REG_SAT 0x03 /* saturation */ +#define REG_CNT 0x05 /* Y contrast */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */ +#define REG_RED_BIAS 0x0D /* red channel bias [5:0] */ +#define REG_GAMMA_COEFF 0x0E /* gamma settings */ +#define REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ +#define REG_EXP 0x10 /* manual exposure setting */ +#define REG_CLOCK 0x11 /* polarity/clock prescaler */ +#define REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ +#define REG_HWIN_START 0x17 /* horizontal window start */ +#define REG_HWIN_END 0x18 /* horizontal window end */ +#define REG_VWIN_START 0x19 /* vertical window start */ +#define REG_VWIN_END 0x1A /* vertical window end */ +#define REG_PIXEL_SHIFT 0x1B /* pixel shift */ +#define REG_YOFFSET 0x21 /* Y channel offset */ +#define REG_UOFFSET 0x22 /* U channel offset */ +#define REG_ECW 0x24 /* exposure white level for AEC */ +#define REG_ECB 0x25 /* exposure black level for AEC */ +#define REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ +#define REG_FRAMERATE_L 0x2B /* frame rate LSB */ +#define REG_ALC 0x2C /* Auto Level Control settings */ +#define REG_VOFFSET 0x2E /* V channel offset adjustment */ +#define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */ +#define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */ +#define REG_BIAS_ADJUST 0x34 /* misc bias settings */ + +/* Window parameters */ +#define HWSBASE 0x38 +#define HWEBASE 0x3a +#define VWSBASE 0x05 +#define VWEBASE 0x05 + +struct ov76be { + int auto_brt; + int auto_exp; + int bandfilt; + int mirror; +}; + +/* NOTE: These are the same as the 7x10 settings, but should eventually be + * optimized for the OV76BE */ +static struct ovcamchip_regvals regvals_init_76be[] = { + { 0x10, 0xff }, + { 0x16, 0x03 }, + { 0x28, 0x24 }, + { 0x2b, 0xac }, + { 0x12, 0x00 }, + { 0x38, 0x81 }, + { 0x28, 0x24 }, /* 0c */ + { 0x0f, 0x85 }, /* lg's setting */ + { 0x15, 0x01 }, + { 0x20, 0x1c }, + { 0x23, 0x2a }, + { 0x24, 0x10 }, + { 0x25, 0x8a }, + { 0x26, 0xa2 }, + { 0x27, 0xc2 }, + { 0x2a, 0x04 }, + { 0x2c, 0xfe }, + { 0x2d, 0x93 }, + { 0x30, 0x71 }, + { 0x31, 0x60 }, + { 0x32, 0x26 }, + { 0x33, 0x20 }, + { 0x34, 0x48 }, + { 0x12, 0x24 }, + { 0x11, 0x01 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* This initializes the OV76be camera chip and relevant variables. */ +static int ov76be_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov76be *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + rc = ov_write_regvals(c, regvals_init_76be); + if (rc < 0) + return rc; + + ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + memset(s, 0, sizeof *s); + + s->auto_brt = 1; + s->auto_exp = 1; + + return rc; +} + +static int ov76be_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov76be_set_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov76be *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_BRIGHT: + rc = ov_write(c, REG_BRT, v >> 8); + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + rc = ov_write(c, REG_EXP, v); + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2b, sixty?0x00:0xac); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x76, 0x01, 0x01); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov76be_get_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov76be *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov76be_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + int qvga = win->quarter; + + /******** QVGA-specific regs ********/ + + ov_write(c, 0x14, qvga?0xa4:0x84); + + /******** Palette-specific regs ********/ + + if (win->format == VIDEO_PALETTE_GREY) { + ov_write_mask(c, 0x0e, 0x40, 0x40); + ov_write_mask(c, 0x13, 0x20, 0x20); + } else { + ov_write_mask(c, 0x0e, 0x00, 0x40); + ov_write_mask(c, 0x13, 0x00, 0x20); + } + + /******** Clock programming ********/ + + ov_write(c, 0x11, win->clockdiv); + + /******** Resolution-specific ********/ + + if (win->width == 640 && win->height == 480) + ov_write(c, 0x35, 0x9e); + else + ov_write(c, 0x35, 0x1e); + + return 0; +} + +static int ov76be_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov76be_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 1; + vwscale = 0; + } else { + hwscale = 2; + vwscale = 1; + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov76be_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov76be_set_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov76be_get_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov76be_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov76be_ops = { + .init = ov76be_init, + .free = ov76be_free, + .command = ov76be_command, +}; diff --git a/drivers/media/video/ovcamchip/ov7x10.c b/drivers/media/video/ovcamchip/ov7x10.c new file mode 100644 index 000000000000..6c383d4b14fa --- /dev/null +++ b/drivers/media/video/ovcamchip/ov7x10.c @@ -0,0 +1,335 @@ +/* OmniVision OV7610/OV7110 Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * Color fixes by by Orion Sky Lawlor (2/26/2000) + * + * 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. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* Registers */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue channel balance */ +#define REG_RED 0x02 /* red channel balance */ +#define REG_SAT 0x03 /* saturation */ +#define REG_CNT 0x05 /* Y contrast */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_BLUE_BIAS 0x0C /* blue channel bias [5:0] */ +#define REG_RED_BIAS 0x0D /* red channel bias [5:0] */ +#define REG_GAMMA_COEFF 0x0E /* gamma settings */ +#define REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ +#define REG_EXP 0x10 /* manual exposure setting */ +#define REG_CLOCK 0x11 /* polarity/clock prescaler */ +#define REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ +#define REG_HWIN_START 0x17 /* horizontal window start */ +#define REG_HWIN_END 0x18 /* horizontal window end */ +#define REG_VWIN_START 0x19 /* vertical window start */ +#define REG_VWIN_END 0x1A /* vertical window end */ +#define REG_PIXEL_SHIFT 0x1B /* pixel shift */ +#define REG_YOFFSET 0x21 /* Y channel offset */ +#define REG_UOFFSET 0x22 /* U channel offset */ +#define REG_ECW 0x24 /* exposure white level for AEC */ +#define REG_ECB 0x25 /* exposure black level for AEC */ +#define REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ +#define REG_FRAMERATE_L 0x2B /* frame rate LSB */ +#define REG_ALC 0x2C /* Auto Level Control settings */ +#define REG_VOFFSET 0x2E /* V channel offset adjustment */ +#define REG_ARRAY_BIAS 0x2F /* array bias -- don't change */ +#define REG_YGAMMA 0x33 /* misc gamma settings [7:6] */ +#define REG_BIAS_ADJUST 0x34 /* misc bias settings */ + +/* Window parameters */ +#define HWSBASE 0x38 +#define HWEBASE 0x3a +#define VWSBASE 0x05 +#define VWEBASE 0x05 + +struct ov7x10 { + int auto_brt; + int auto_exp; + int bandfilt; + int mirror; +}; + +/* Lawrence Glaister reports: + * + * Register 0x0f in the 7610 has the following effects: + * + * 0x85 (AEC method 1): Best overall, good contrast range + * 0x45 (AEC method 2): Very overexposed + * 0xa5 (spec sheet default): Ok, but the black level is + * shifted resulting in loss of contrast + * 0x05 (old driver setting): very overexposed, too much + * contrast + */ +static struct ovcamchip_regvals regvals_init_7x10[] = { + { 0x10, 0xff }, + { 0x16, 0x03 }, + { 0x28, 0x24 }, + { 0x2b, 0xac }, + { 0x12, 0x00 }, + { 0x38, 0x81 }, + { 0x28, 0x24 }, /* 0c */ + { 0x0f, 0x85 }, /* lg's setting */ + { 0x15, 0x01 }, + { 0x20, 0x1c }, + { 0x23, 0x2a }, + { 0x24, 0x10 }, + { 0x25, 0x8a }, + { 0x26, 0xa2 }, + { 0x27, 0xc2 }, + { 0x2a, 0x04 }, + { 0x2c, 0xfe }, + { 0x2d, 0x93 }, + { 0x30, 0x71 }, + { 0x31, 0x60 }, + { 0x32, 0x26 }, + { 0x33, 0x20 }, + { 0x34, 0x48 }, + { 0x12, 0x24 }, + { 0x11, 0x01 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* This initializes the OV7x10 camera chip and relevant variables. */ +static int ov7x10_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x10 *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + rc = ov_write_regvals(c, regvals_init_7x10); + if (rc < 0) + return rc; + + ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + memset(s, 0, sizeof *s); + + s->auto_brt = 1; + s->auto_exp = 1; + + return rc; +} + +static int ov7x10_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov7x10_set_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x10 *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_write(c, REG_CNT, v >> 8); + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_write(c, REG_BRT, v >> 8); + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_HUE: + rc = ov_write(c, REG_RED, 0xFF - (v >> 8)); + if (rc < 0) + goto out; + + rc = ov_write(c, REG_BLUE, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + rc = ov_write(c, REG_EXP, v); + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2b, sixty?0x00:0xac); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x13, 0x10, 0x10); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x13, 0x00, 0x10); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x29, v?0x00:0x80, 0x80); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov7x10_get_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x10 *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_read(c, REG_CNT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_HUE: + rc = ov_read(c, REG_BLUE, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov7x10_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + int qvga = win->quarter; + + /******** QVGA-specific regs ********/ + + ov_write(c, 0x14, qvga?0x24:0x04); + + /******** Palette-specific regs ********/ + + if (win->format == VIDEO_PALETTE_GREY) { + ov_write_mask(c, 0x0e, 0x40, 0x40); + ov_write_mask(c, 0x13, 0x20, 0x20); + } else { + ov_write_mask(c, 0x0e, 0x00, 0x40); + ov_write_mask(c, 0x13, 0x00, 0x20); + } + + /******** Clock programming ********/ + + ov_write(c, 0x11, win->clockdiv); + + /******** Resolution-specific ********/ + + if (win->width == 640 && win->height == 480) + ov_write(c, 0x35, 0x9e); + else + ov_write(c, 0x35, 0x1e); + + return 0; +} + +static int ov7x10_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov7x10_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 1; + vwscale = 0; + } else { + hwscale = 2; + vwscale = 1; + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov7x10_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov7x10_set_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov7x10_get_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov7x10_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov7x10_ops = { + .init = ov7x10_init, + .free = ov7x10_free, + .command = ov7x10_command, +}; diff --git a/drivers/media/video/ovcamchip/ov7x20.c b/drivers/media/video/ovcamchip/ov7x20.c new file mode 100644 index 000000000000..3c8c48f338ba --- /dev/null +++ b/drivers/media/video/ovcamchip/ov7x20.c @@ -0,0 +1,455 @@ +/* OmniVision OV7620/OV7120 Camera Chip Support Code + * + * Copyright (c) 1999-2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * OV7620 fixes by Charl P. Botha + * + * 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. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include "ovcamchip_priv.h" + +/* Registers */ +#define REG_GAIN 0x00 /* gain [5:0] */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_SAT 0x03 /* saturation */ +#define REG_BRT 0x06 /* Y brightness */ +#define REG_SHARP 0x07 /* analog sharpness */ +#define REG_BLUE_BIAS 0x0C /* WB blue ratio [5:0] */ +#define REG_RED_BIAS 0x0D /* WB red ratio [5:0] */ +#define REG_EXP 0x10 /* exposure */ + +/* Default control settings. Values are in terms of V4L2 controls. */ +#define OV7120_DFL_BRIGHT 0x60 +#define OV7620_DFL_BRIGHT 0x60 +#define OV7120_DFL_SAT 0xb0 +#define OV7620_DFL_SAT 0xc0 +#define DFL_AUTO_EXP 1 +#define DFL_AUTO_GAIN 1 +#define OV7120_DFL_GAIN 0x00 +#define OV7620_DFL_GAIN 0x00 +/* NOTE: Since autoexposure is the default, these aren't programmed into the + * OV7x20 chip. They are just here because V4L2 expects a default */ +#define OV7120_DFL_EXP 0x7f +#define OV7620_DFL_EXP 0x7f + +/* Window parameters */ +#define HWSBASE 0x2F /* From 7620.SET (spec is wrong) */ +#define HWEBASE 0x2F +#define VWSBASE 0x05 +#define VWEBASE 0x05 + +struct ov7x20 { + int auto_brt; + int auto_exp; + int auto_gain; + int backlight; + int bandfilt; + int mirror; +}; + +/* Contrast look-up table */ +static unsigned char ctab[] = { + 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, + 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff +}; + +/* Settings for (Black & White) OV7120 camera chip */ +static struct ovcamchip_regvals regvals_init_7120[] = { + { 0x12, 0x80 }, /* reset */ + { 0x13, 0x00 }, /* Autoadjust off */ + { 0x12, 0x20 }, /* Disable AWB */ + { 0x13, DFL_AUTO_GAIN?0x01:0x00 }, /* Autoadjust on (if desired) */ + { 0x00, OV7120_DFL_GAIN }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x03, OV7120_DFL_SAT }, + { 0x06, OV7120_DFL_BRIGHT }, + { 0x07, 0x00 }, + { 0x0c, 0x20 }, + { 0x0d, 0x20 }, + { 0x11, 0x01 }, + { 0x14, 0x84 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x2f }, + { 0x18, 0xcf }, + { 0x19, 0x06 }, + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0x08 }, + { 0x21, 0x80 }, + { 0x22, 0x80 }, + { 0x23, 0x00 }, + { 0x26, 0xa0 }, + { 0x27, 0xfa }, + { 0x28, 0x20 }, /* DON'T set bit 6. It is for the OV7620 only */ + { 0x29, DFL_AUTO_EXP?0x00:0x80 }, + { 0x2a, 0x10 }, + { 0x2b, 0x00 }, + { 0x2c, 0x88 }, + { 0x2d, 0x95 }, + { 0x2e, 0x80 }, + { 0x2f, 0x44 }, + { 0x60, 0x20 }, + { 0x61, 0x02 }, + { 0x62, 0x5f }, + { 0x63, 0xd5 }, + { 0x64, 0x57 }, + { 0x65, 0x83 }, /* OV says "don't change this value" */ + { 0x66, 0x55 }, + { 0x67, 0x92 }, + { 0x68, 0xcf }, + { 0x69, 0x76 }, + { 0x6a, 0x22 }, + { 0x6b, 0xe2 }, + { 0x6c, 0x40 }, + { 0x6d, 0x48 }, + { 0x6e, 0x80 }, + { 0x6f, 0x0d }, + { 0x70, 0x89 }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0xa0 }, + { 0x75, 0x8e }, + { 0x76, 0x00 }, + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0xe6 }, + { 0x7c, 0x00 }, + { 0x24, 0x3a }, + { 0x25, 0x60 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* Settings for (color) OV7620 camera chip */ +static struct ovcamchip_regvals regvals_init_7620[] = { + { 0x12, 0x80 }, /* reset */ + { 0x00, OV7620_DFL_GAIN }, + { 0x01, 0x80 }, + { 0x02, 0x80 }, + { 0x03, OV7620_DFL_SAT }, + { 0x06, OV7620_DFL_BRIGHT }, + { 0x07, 0x00 }, + { 0x0c, 0x24 }, + { 0x0c, 0x24 }, + { 0x0d, 0x24 }, + { 0x11, 0x01 }, + { 0x12, 0x24 }, + { 0x13, DFL_AUTO_GAIN?0x01:0x00 }, + { 0x14, 0x84 }, + { 0x15, 0x01 }, + { 0x16, 0x03 }, + { 0x17, 0x2f }, + { 0x18, 0xcf }, + { 0x19, 0x06 }, + { 0x1a, 0xf5 }, + { 0x1b, 0x00 }, + { 0x20, 0x18 }, + { 0x21, 0x80 }, + { 0x22, 0x80 }, + { 0x23, 0x00 }, + { 0x26, 0xa2 }, + { 0x27, 0xea }, + { 0x28, 0x20 }, + { 0x29, DFL_AUTO_EXP?0x00:0x80 }, + { 0x2a, 0x10 }, + { 0x2b, 0x00 }, + { 0x2c, 0x88 }, + { 0x2d, 0x91 }, + { 0x2e, 0x80 }, + { 0x2f, 0x44 }, + { 0x60, 0x27 }, + { 0x61, 0x02 }, + { 0x62, 0x5f }, + { 0x63, 0xd5 }, + { 0x64, 0x57 }, + { 0x65, 0x83 }, + { 0x66, 0x55 }, + { 0x67, 0x92 }, + { 0x68, 0xcf }, + { 0x69, 0x76 }, + { 0x6a, 0x22 }, + { 0x6b, 0x00 }, + { 0x6c, 0x02 }, + { 0x6d, 0x44 }, + { 0x6e, 0x80 }, + { 0x6f, 0x1d }, + { 0x70, 0x8b }, + { 0x71, 0x00 }, + { 0x72, 0x14 }, + { 0x73, 0x54 }, + { 0x74, 0x00 }, + { 0x75, 0x8e }, + { 0x76, 0x00 }, + { 0x77, 0xff }, + { 0x78, 0x80 }, + { 0x79, 0x80 }, + { 0x7a, 0x80 }, + { 0x7b, 0xe2 }, + { 0x7c, 0x00 }, + { 0xff, 0xff }, /* END MARKER */ +}; + +/* Returns index into the specified look-up table, with 'n' elements, for which + * the value is greater than or equal to "val". If a match isn't found, (n-1) + * is returned. The entries in the table must be in ascending order. */ +static inline int ov7x20_lut_find(unsigned char lut[], int n, unsigned char val) +{ + int i = 0; + + while (lut[i] < val && i < n) + i++; + + return i; +} + +/* This initializes the OV7x20 camera chip and relevant variables. */ +static int ov7x20_init(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x20 *s; + int rc; + + DDEBUG(4, &c->dev, "entered"); + + if (ov->mono) + rc = ov_write_regvals(c, regvals_init_7120); + else + rc = ov_write_regvals(c, regvals_init_7620); + + if (rc < 0) + return rc; + + ov->spriv = s = kmalloc(sizeof *s, GFP_KERNEL); + if (!s) + return -ENOMEM; + memset(s, 0, sizeof *s); + + s->auto_brt = 1; + s->auto_exp = DFL_AUTO_EXP; + s->auto_gain = DFL_AUTO_GAIN; + + return 0; +} + +static int ov7x20_free(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + kfree(ov->spriv); + return 0; +} + +static int ov7x20_set_v4l1_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x20 *s = ov->spriv; + int rc; + int v = ctl->value; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + { + /* Use Y gamma control instead. Bit 0 enables it. */ + rc = ov_write(c, 0x64, ctab[v >> 12]); + break; + } + case OVCAMCHIP_CID_BRIGHT: + /* 7620 doesn't like manual changes when in auto mode */ + if (!s->auto_brt) + rc = ov_write(c, REG_BRT, v >> 8); + else + rc = 0; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_write(c, REG_SAT, v >> 8); + break; + case OVCAMCHIP_CID_EXP: + if (!s->auto_exp) + rc = ov_write(c, REG_EXP, v); + else + rc = -EBUSY; + break; + case OVCAMCHIP_CID_FREQ: + { + int sixty = (v == 60); + + rc = ov_write_mask(c, 0x2a, sixty?0x00:0x80, 0x80); + if (rc < 0) + goto out; + + rc = ov_write(c, 0x2b, sixty?0x00:0xac); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x76, 0x01, 0x01); + break; + } + case OVCAMCHIP_CID_BANDFILT: + rc = ov_write_mask(c, 0x2d, v?0x04:0x00, 0x04); + s->bandfilt = v; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + rc = ov_write_mask(c, 0x2d, v?0x10:0x00, 0x10); + s->auto_brt = v; + break; + case OVCAMCHIP_CID_AUTOEXP: + rc = ov_write_mask(c, 0x13, v?0x01:0x00, 0x01); + s->auto_exp = v; + break; + case OVCAMCHIP_CID_BACKLIGHT: + { + rc = ov_write_mask(c, 0x68, v?0xe0:0xc0, 0xe0); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x29, v?0x08:0x00, 0x08); + if (rc < 0) + goto out; + + rc = ov_write_mask(c, 0x28, v?0x02:0x00, 0x02); + s->backlight = v; + break; + } + case OVCAMCHIP_CID_MIRROR: + rc = ov_write_mask(c, 0x12, v?0x40:0x00, 0x40); + s->mirror = v; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + +out: + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, v, rc); + return rc; +} + +static int ov7x20_get_v4l1_control(struct i2c_client *c, + struct ovcamchip_control *ctl) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + struct ov7x20 *s = ov->spriv; + int rc = 0; + unsigned char val = 0; + + switch (ctl->id) { + case OVCAMCHIP_CID_CONT: + rc = ov_read(c, 0x64, &val); + ctl->value = ov7x20_lut_find(ctab, 16, val) << 12; + break; + case OVCAMCHIP_CID_BRIGHT: + rc = ov_read(c, REG_BRT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_SAT: + rc = ov_read(c, REG_SAT, &val); + ctl->value = val << 8; + break; + case OVCAMCHIP_CID_EXP: + rc = ov_read(c, REG_EXP, &val); + ctl->value = val; + break; + case OVCAMCHIP_CID_BANDFILT: + ctl->value = s->bandfilt; + break; + case OVCAMCHIP_CID_AUTOBRIGHT: + ctl->value = s->auto_brt; + break; + case OVCAMCHIP_CID_AUTOEXP: + ctl->value = s->auto_exp; + break; + case OVCAMCHIP_CID_BACKLIGHT: + ctl->value = s->backlight; + break; + case OVCAMCHIP_CID_MIRROR: + ctl->value = s->mirror; + break; + default: + DDEBUG(2, &c->dev, "control not supported: %d", ctl->id); + return -EPERM; + } + + DDEBUG(3, &c->dev, "id=%d, arg=%d, rc=%d", ctl->id, ctl->value, rc); + return rc; +} + +static int ov7x20_mode_init(struct i2c_client *c, struct ovcamchip_window *win) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + int qvga = win->quarter; + + /******** QVGA-specific regs ********/ + ov_write_mask(c, 0x14, qvga?0x20:0x00, 0x20); + ov_write_mask(c, 0x28, qvga?0x00:0x20, 0x20); + ov_write(c, 0x24, qvga?0x20:0x3a); + ov_write(c, 0x25, qvga?0x30:0x60); + ov_write_mask(c, 0x2d, qvga?0x40:0x00, 0x40); + if (!ov->mono) + ov_write_mask(c, 0x67, qvga?0xf0:0x90, 0xf0); + ov_write_mask(c, 0x74, qvga?0x20:0x00, 0x20); + + /******** Clock programming ********/ + + ov_write(c, 0x11, win->clockdiv); + + return 0; +} + +static int ov7x20_set_window(struct i2c_client *c, struct ovcamchip_window *win) +{ + int ret, hwscale, vwscale; + + ret = ov7x20_mode_init(c, win); + if (ret < 0) + return ret; + + if (win->quarter) { + hwscale = 1; + vwscale = 0; + } else { + hwscale = 2; + vwscale = 1; + } + + ov_write(c, 0x17, HWSBASE + (win->x >> hwscale)); + ov_write(c, 0x18, HWEBASE + ((win->x + win->width) >> hwscale)); + ov_write(c, 0x19, VWSBASE + (win->y >> vwscale)); + ov_write(c, 0x1a, VWEBASE + ((win->y + win->height) >> vwscale)); + + return 0; +} + +static int ov7x20_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + switch (cmd) { + case OVCAMCHIP_CMD_S_CTRL: + return ov7x20_set_v4l1_control(c, arg); + case OVCAMCHIP_CMD_G_CTRL: + return ov7x20_get_v4l1_control(c, arg); + case OVCAMCHIP_CMD_S_MODE: + return ov7x20_set_window(c, arg); + default: + DDEBUG(2, &c->dev, "command not supported: %d", cmd); + return -ENOIOCTLCMD; + } +} + +struct ovcamchip_ops ov7x20_ops = { + .init = ov7x20_init, + .free = ov7x20_free, + .command = ov7x20_command, +}; diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c new file mode 100644 index 000000000000..d88956a26aee --- /dev/null +++ b/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -0,0 +1,446 @@ +/* Shared Code for OmniVision Camera Chip Drivers + * + * Copyright (c) 2004 Mark McClelland + * http://alpha.dyndns.org/ov511/ + * + * 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. NO WARRANTY OF ANY KIND is expressed or implied. + */ + +#define DEBUG + +#include +#include +#include +#include +#include "ovcamchip_priv.h" + +#define DRIVER_VERSION "v2.27 for Linux 2.6" +#define DRIVER_AUTHOR "Mark McClelland " +#define DRIVER_DESC "OV camera chip I2C driver" + +#define PINFO(fmt, args...) printk(KERN_INFO "ovcamchip: " fmt "\n" , ## args); +#define PERROR(fmt, args...) printk(KERN_ERR "ovcamchip: " fmt "\n" , ## args); + +#ifdef DEBUG +int ovcamchip_debug = 0; +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, + "Debug level: 0=none, 1=inits, 2=warning, 3=config, 4=functions, 5=all"); +#endif + +/* By default, let bridge driver tell us if chip is monochrome. mono=0 + * will ignore that and always treat chips as color. mono=1 will force + * monochrome mode for all chips. */ +static int mono = -1; +module_param(mono, int, 0); +MODULE_PARM_DESC(mono, + "1=chips are monochrome (OVx1xx), 0=force color, -1=autodetect (default)"); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* Registers common to all chips, that are needed for detection */ +#define GENERIC_REG_ID_HIGH 0x1C /* manufacturer ID MSB */ +#define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */ +#define GENERIC_REG_COM_I 0x29 /* misc ID bits */ + +extern struct ovcamchip_ops ov6x20_ops; +extern struct ovcamchip_ops ov6x30_ops; +extern struct ovcamchip_ops ov7x10_ops; +extern struct ovcamchip_ops ov7x20_ops; +extern struct ovcamchip_ops ov76be_ops; + +static char *chip_names[NUM_CC_TYPES] = { + [CC_UNKNOWN] = "Unknown chip", + [CC_OV76BE] = "OV76BE", + [CC_OV7610] = "OV7610", + [CC_OV7620] = "OV7620", + [CC_OV7620AE] = "OV7620AE", + [CC_OV6620] = "OV6620", + [CC_OV6630] = "OV6630", + [CC_OV6630AE] = "OV6630AE", + [CC_OV6630AF] = "OV6630AF", +}; + +/* Forward declarations */ +static struct i2c_driver driver; +static struct i2c_client client_template; + +/* ----------------------------------------------------------------------- */ + +int ov_write_regvals(struct i2c_client *c, struct ovcamchip_regvals *rvals) +{ + int rc; + + while (rvals->reg != 0xff) { + rc = ov_write(c, rvals->reg, rvals->val); + if (rc < 0) + return rc; + rvals++; + } + + return 0; +} + +/* Writes bits at positions specified by mask to an I2C reg. Bits that are in + * the same position as 1's in "mask" are cleared and set to "value". Bits + * that are in the same position as 0's in "mask" are preserved, regardless + * of their respective state in "value". + */ +int ov_write_mask(struct i2c_client *c, + unsigned char reg, + unsigned char value, + unsigned char mask) +{ + int rc; + unsigned char oldval, newval; + + if (mask == 0xff) { + newval = value; + } else { + rc = ov_read(c, reg, &oldval); + if (rc < 0) + return rc; + + oldval &= (~mask); /* Clear the masked bits */ + value &= mask; /* Enforce mask on value */ + newval = oldval | value; /* Set the desired bits */ + } + + return ov_write(c, reg, newval); +} + +/* ----------------------------------------------------------------------- */ + +/* Reset the chip and ensure that I2C is synchronized. Returns <0 if failure. + */ +static int init_camchip(struct i2c_client *c) +{ + int i, success; + unsigned char high, low; + + /* Reset the chip */ + ov_write(c, 0x12, 0x80); + + /* Wait for it to initialize */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1 + 150 * HZ / 1000); + + for (i = 0, success = 0; i < I2C_DETECT_RETRIES && !success; i++) { + if (ov_read(c, GENERIC_REG_ID_HIGH, &high) >= 0) { + if (ov_read(c, GENERIC_REG_ID_LOW, &low) >= 0) { + if (high == 0x7F && low == 0xA2) { + success = 1; + continue; + } + } + } + + /* Reset the chip */ + ov_write(c, 0x12, 0x80); + + /* Wait for it to initialize */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1 + 150 * HZ / 1000); + + /* Dummy read to sync I2C */ + ov_read(c, 0x00, &low); + } + + if (!success) + return -EIO; + + PDEBUG(1, "I2C synced in %d attempt(s)", i); + + return 0; +} + +/* This detects the OV7610, OV7620, or OV76BE chip. */ +static int ov7xx0_detect(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + int rc; + unsigned char val; + + PDEBUG(4, ""); + + /* Detect chip (sub)type */ + rc = ov_read(c, GENERIC_REG_COM_I, &val); + if (rc < 0) { + PERROR("Error detecting ov7xx0 type"); + return rc; + } + + if ((val & 3) == 3) { + PINFO("Camera chip is an OV7610"); + ov->subtype = CC_OV7610; + } else if ((val & 3) == 1) { + rc = ov_read(c, 0x15, &val); + if (rc < 0) { + PERROR("Error detecting ov7xx0 type"); + return rc; + } + + if (val & 1) { + PINFO("Camera chip is an OV7620AE"); + /* OV7620 is a close enough match for now. There are + * some definite differences though, so this should be + * fixed */ + ov->subtype = CC_OV7620; + } else { + PINFO("Camera chip is an OV76BE"); + ov->subtype = CC_OV76BE; + } + } else if ((val & 3) == 0) { + PINFO("Camera chip is an OV7620"); + ov->subtype = CC_OV7620; + } else { + PERROR("Unknown camera chip version: %d", val & 3); + return -ENOSYS; + } + + if (ov->subtype == CC_OV76BE) + ov->sops = &ov76be_ops; + else if (ov->subtype == CC_OV7620) + ov->sops = &ov7x20_ops; + else + ov->sops = &ov7x10_ops; + + return 0; +} + +/* This detects the OV6620, OV6630, OV6630AE, or OV6630AF chip. */ +static int ov6xx0_detect(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + int rc; + unsigned char val; + + PDEBUG(4, ""); + + /* Detect chip (sub)type */ + rc = ov_read(c, GENERIC_REG_COM_I, &val); + if (rc < 0) { + PERROR("Error detecting ov6xx0 type"); + return -1; + } + + if ((val & 3) == 0) { + ov->subtype = CC_OV6630; + PINFO("Camera chip is an OV6630"); + } else if ((val & 3) == 1) { + ov->subtype = CC_OV6620; + PINFO("Camera chip is an OV6620"); + } else if ((val & 3) == 2) { + ov->subtype = CC_OV6630; + PINFO("Camera chip is an OV6630AE"); + } else if ((val & 3) == 3) { + ov->subtype = CC_OV6630; + PINFO("Camera chip is an OV6630AF"); + } + + if (ov->subtype == CC_OV6620) + ov->sops = &ov6x20_ops; + else + ov->sops = &ov6x30_ops; + + return 0; +} + +static int ovcamchip_detect(struct i2c_client *c) +{ + /* Ideally we would just try a single register write and see if it NAKs. + * That isn't possible since the OV518 can't report I2C transaction + * failures. So, we have to try to initialize the chip (i.e. reset it + * and check the ID registers) to detect its presence. */ + + /* Test for 7xx0 */ + PDEBUG(3, "Testing for 0V7xx0"); + c->addr = OV7xx0_SID; + if (init_camchip(c) < 0) { + /* Test for 6xx0 */ + PDEBUG(3, "Testing for 0V6xx0"); + c->addr = OV6xx0_SID; + if (init_camchip(c) < 0) { + return -ENODEV; + } else { + if (ov6xx0_detect(c) < 0) { + PERROR("Failed to init OV6xx0"); + return -EIO; + } + } + } else { + if (ov7xx0_detect(c) < 0) { + PERROR("Failed to init OV7xx0"); + return -EIO; + } + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static int ovcamchip_attach(struct i2c_adapter *adap) +{ + int rc = 0; + struct ovcamchip *ov; + struct i2c_client *c; + + /* I2C is not a PnP bus, so we can never be certain that we're talking + * to the right chip. To prevent damage to EEPROMS and such, only + * attach to adapters that are known to contain OV camera chips. */ + + switch (adap->id) { + case (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV511): + case (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OV518): + case (I2C_ALGO_SMBUS | I2C_HW_SMBUS_OVFX2): + case (I2C_ALGO_SMBUS | I2C_HW_SMBUS_W9968CF): + PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id); + break; + default: + PDEBUG(1, "Adapter ID 0x%06x rejected", adap->id); + return -ENODEV; + } + + c = kmalloc(sizeof *c, GFP_KERNEL); + if (!c) { + rc = -ENOMEM; + goto no_client; + } + memcpy(c, &client_template, sizeof *c); + c->adapter = adap; + strcpy(i2c_clientname(c), "OV????"); + + ov = kmalloc(sizeof *ov, GFP_KERNEL); + if (!ov) { + rc = -ENOMEM; + goto no_ov; + } + memset(ov, 0, sizeof *ov); + i2c_set_clientdata(c, ov); + + rc = ovcamchip_detect(c); + if (rc < 0) + goto error; + + strcpy(i2c_clientname(c), chip_names[ov->subtype]); + + PDEBUG(1, "Camera chip detection complete"); + + i2c_attach_client(c); + + return rc; +error: + kfree(ov); +no_ov: + kfree(c); +no_client: + PDEBUG(1, "returning %d", rc); + return rc; +} + +static int ovcamchip_detach(struct i2c_client *c) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + int rc; + + rc = ov->sops->free(c); + if (rc < 0) + return rc; + + i2c_detach_client(c); + + kfree(ov); + kfree(c); + return 0; +} + +static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg) +{ + struct ovcamchip *ov = i2c_get_clientdata(c); + + if (!ov->initialized && + cmd != OVCAMCHIP_CMD_Q_SUBTYPE && + cmd != OVCAMCHIP_CMD_INITIALIZE) { + dev_err(&c->dev, "ERROR: Camera chip not initialized yet!\n"); + return -EPERM; + } + + switch (cmd) { + case OVCAMCHIP_CMD_Q_SUBTYPE: + { + *(int *)arg = ov->subtype; + return 0; + } + case OVCAMCHIP_CMD_INITIALIZE: + { + int rc; + + if (mono == -1) + ov->mono = *(int *)arg; + else + ov->mono = mono; + + if (ov->mono) { + if (ov->subtype != CC_OV7620) + dev_warn(&c->dev, "Warning: Monochrome not " + "implemented for this chip\n"); + else + dev_info(&c->dev, "Initializing chip as " + "monochrome\n"); + } + + rc = ov->sops->init(c); + if (rc < 0) + return rc; + + ov->initialized = 1; + return 0; + } + default: + return ov->sops->command(c, cmd, arg); + } +} + +/* ----------------------------------------------------------------------- */ + +static struct i2c_driver driver = { + .owner = THIS_MODULE, + .name = "ovcamchip", + .id = I2C_DRIVERID_OVCAMCHIP, + .class = I2C_CLASS_CAM_DIGITAL, + .flags = I2C_DF_NOTIFY, + .attach_adapter = ovcamchip_attach, + .detach_client = ovcamchip_detach, + .command = ovcamchip_command, +}; + +static struct i2c_client client_template = { + I2C_DEVNAME("(unset)"), + .id = -1, + .driver = &driver, +}; + +static int __init ovcamchip_init(void) +{ +#ifdef DEBUG + ovcamchip_debug = debug; +#endif + + PINFO(DRIVER_VERSION " : " DRIVER_DESC); + return i2c_add_driver(&driver); +} + +static void __exit ovcamchip_exit(void) +{ + i2c_del_driver(&driver); +} + +module_init(ovcamchip_init); +module_exit(ovcamchip_exit); diff --git a/drivers/media/video/ovcamchip/ovcamchip_priv.h b/drivers/media/video/ovcamchip/ovcamchip_priv.h new file mode 100644 index 000000000000..575e612a5546 --- /dev/null +++ b/drivers/media/video/ovcamchip/ovcamchip_priv.h @@ -0,0 +1,87 @@ +/* OmniVision* camera chip driver private definitions for core code and + * chip-specific code + * + * Copyright (c) 1999-2004 Mark McClelland + * + * 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. NO WARRANTY OF ANY KIND is expressed or implied. + * + * * OmniVision is a trademark of OmniVision Technologies, Inc. This driver + * is not sponsored or developed by them. + */ + +#ifndef __LINUX_OVCAMCHIP_PRIV_H +#define __LINUX_OVCAMCHIP_PRIV_H + +#include + +#ifdef DEBUG +extern int ovcamchip_debug; +#endif + +#define PDEBUG(level, fmt, args...) \ + if (ovcamchip_debug >= (level)) pr_debug("[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) + +#define DDEBUG(level, dev, fmt, args...) \ + if (ovcamchip_debug >= (level)) dev_dbg(dev, "[%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args) + +/* Number of times to retry chip detection. Increase this if you are getting + * "Failed to init camera chip" */ +#define I2C_DETECT_RETRIES 10 + +struct ovcamchip_regvals { + unsigned char reg; + unsigned char val; +}; + +struct ovcamchip_ops { + int (*init)(struct i2c_client *); + int (*free)(struct i2c_client *); + int (*command)(struct i2c_client *, unsigned int, void *); +}; + +struct ovcamchip { + struct ovcamchip_ops *sops; + void *spriv; /* Private data for OV7x10.c etc... */ + int subtype; /* = SEN_OV7610 etc... */ + int mono; /* Monochrome chip? (invalid until init) */ + int initialized; /* OVCAMCHIP_CMD_INITIALIZE was successful */ +}; + +/* --------------------------------- */ +/* I2C I/O */ +/* --------------------------------- */ + +static inline int ov_read(struct i2c_client *c, unsigned char reg, + unsigned char *value) +{ + int rc; + + rc = i2c_smbus_read_byte_data(c, reg); + *value = (unsigned char) rc; + return rc; +} + +static inline int ov_write(struct i2c_client *c, unsigned char reg, + unsigned char value ) +{ + return i2c_smbus_write_byte_data(c, reg, value); +} + +/* --------------------------------- */ +/* FUNCTION PROTOTYPES */ +/* --------------------------------- */ + +/* Functions in ovcamchip_core.c */ + +extern int ov_write_regvals(struct i2c_client *c, + struct ovcamchip_regvals *rvals); + +extern int ov_write_mask(struct i2c_client *c, unsigned char reg, + unsigned char value, unsigned char mask); + +#endif diff --git a/include/media/ovcamchip.h b/include/media/ovcamchip.h new file mode 100644 index 000000000000..cb7c0aa96f22 --- /dev/null +++ b/include/media/ovcamchip.h @@ -0,0 +1,104 @@ +/* OmniVision* camera chip driver API + * + * Copyright (c) 1999-2004 Mark McClelland + * + * 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. NO WARRANTY OF ANY KIND is expressed or implied. + * + * * OmniVision is a trademark of OmniVision Technologies, Inc. This driver + * is not sponsored or developed by them. + */ + +#ifndef __LINUX_OVCAMCHIP_H +#define __LINUX_OVCAMCHIP_H + +#include +#include + +/* Remove these once they are officially defined */ +#ifndef I2C_DRIVERID_OVCAMCHIP + #define I2C_DRIVERID_OVCAMCHIP 0xf00f +#endif +#ifndef I2C_HW_SMBUS_OV511 + #define I2C_HW_SMBUS_OV511 0xfe +#endif +#ifndef I2C_HW_SMBUS_OV518 + #define I2C_HW_SMBUS_OV518 0xff +#endif +#ifndef I2C_HW_SMBUS_OVFX2 + #define I2C_HW_SMBUS_OVFX2 0xfd +#endif + +/* --------------------------------- */ +/* ENUMERATIONS */ +/* --------------------------------- */ + +/* Controls */ +enum { + OVCAMCHIP_CID_CONT, /* Contrast */ + OVCAMCHIP_CID_BRIGHT, /* Brightness */ + OVCAMCHIP_CID_SAT, /* Saturation */ + OVCAMCHIP_CID_HUE, /* Hue */ + OVCAMCHIP_CID_EXP, /* Exposure */ + OVCAMCHIP_CID_FREQ, /* Light frequency */ + OVCAMCHIP_CID_BANDFILT, /* Banding filter */ + OVCAMCHIP_CID_AUTOBRIGHT, /* Auto brightness */ + OVCAMCHIP_CID_AUTOEXP, /* Auto exposure */ + OVCAMCHIP_CID_BACKLIGHT, /* Back light compensation */ + OVCAMCHIP_CID_MIRROR, /* Mirror horizontally */ +}; + +/* Chip types */ +#define NUM_CC_TYPES 9 +enum { + CC_UNKNOWN, + CC_OV76BE, + CC_OV7610, + CC_OV7620, + CC_OV7620AE, + CC_OV6620, + CC_OV6630, + CC_OV6630AE, + CC_OV6630AF, +}; + +/* --------------------------------- */ +/* I2C ADDRESSES */ +/* --------------------------------- */ + +#define OV7xx0_SID (0x42 >> 1) +#define OV6xx0_SID (0xC0 >> 1) + +/* --------------------------------- */ +/* API */ +/* --------------------------------- */ + +struct ovcamchip_control { + __u32 id; + __s32 value; +}; + +struct ovcamchip_window { + int x; + int y; + int width; + int height; + int format; + int quarter; /* Scale width and height down 2x */ + + /* This stuff will be removed eventually */ + int clockdiv; /* Clock divisor setting */ +}; + +/* Commands */ +#define OVCAMCHIP_CMD_Q_SUBTYPE _IOR (0x88, 0x00, int) +#define OVCAMCHIP_CMD_INITIALIZE _IOW (0x88, 0x01, int) +/* You must call OVCAMCHIP_CMD_INITIALIZE before any of commands below! */ +#define OVCAMCHIP_CMD_S_CTRL _IOW (0x88, 0x02, struct ovcamchip_control) +#define OVCAMCHIP_CMD_G_CTRL _IOWR (0x88, 0x03, struct ovcamchip_control) +#define OVCAMCHIP_CMD_S_MODE _IOW (0x88, 0x04, struct ovcamchip_window) +#define OVCAMCHIP_MAX_CMD _IO (0x88, 0x3f) + +#endif -- cgit v1.2.3 From c87c2fe1694f4a6435eab283ac2c752a72e6dd2e Mon Sep 17 00:00:00 2001 From: Mika Kukkonen Date: Sun, 20 Jun 2004 04:54:18 -0700 Subject: [PATCH] Uninline machine_specific_memory_setup() Cleanup arch/i386/kernel/setup.c a little bit by: * un-inlining machine_specific_memory_setup() (it's implementations are pretty big to be inlined anyway) * remove setup_memory_region() by moving the code inside setup_arch() I would also recommend BK-renaming all four files (include/asm-i386/*/setup_arch_post.h) to ".c" and moving them into arch/i386/*/, but that obviously is not needed in anyway. But IMHO they are clearly ".c" files, not ".h" files. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/i386/kernel/setup.c | 18 ++++++------------ include/asm-i386/mach-default/setup_arch_post.h | 2 +- include/asm-i386/mach-visws/setup_arch_post.h | 2 +- include/asm-i386/mach-voyager/setup_arch_post.h | 2 +- 4 files changed, 9 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 95e9fa75efb6..fc466fb4488a 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -57,8 +57,6 @@ unsigned long init_pg_tables_end __initdata = ~0UL; int disable_pse __initdata = 0; -static inline char * __init machine_specific_memory_setup(void); - /* * Machine setup.. */ @@ -660,14 +658,6 @@ static inline void copy_edd(void) */ #define LOWMEMSIZE() (0x9f000) -static void __init setup_memory_region(void) -{ - char *who = machine_specific_memory_setup(); - printk(KERN_INFO "BIOS-provided physical RAM map:\n"); - print_memory_map(who); -} /* setup_memory_region */ - - static void __init parse_cmdline_early (char ** cmdline_p) { char c = ' ', *to = command_line, *from = saved_command_line; @@ -1270,6 +1260,8 @@ static int __init noreplacement_setup(char *s) __setup("noreplacement", noreplacement_setup); +static char * __init machine_specific_memory_setup(void); + /* * 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 @@ -1320,8 +1312,10 @@ void __init setup_arch(char **cmdline_p) ARCH_SETUP if (efi_enabled) efi_init(); - else - setup_memory_region(); + else { + printk(KERN_INFO "BIOS-provided physical RAM map:\n"); + print_memory_map(machine_specific_memory_setup()); + } copy_edd(); diff --git a/include/asm-i386/mach-default/setup_arch_post.h b/include/asm-i386/mach-default/setup_arch_post.h index 43b3b2d1fa11..2fc4888721f6 100644 --- a/include/asm-i386/mach-default/setup_arch_post.h +++ b/include/asm-i386/mach-default/setup_arch_post.h @@ -6,7 +6,7 @@ * use of all of the static functions. **/ -static inline char * __init machine_specific_memory_setup(void) +static char * __init machine_specific_memory_setup(void) { char *who; diff --git a/include/asm-i386/mach-visws/setup_arch_post.h b/include/asm-i386/mach-visws/setup_arch_post.h index 2857daf5af40..cdbd895a54b1 100644 --- a/include/asm-i386/mach-visws/setup_arch_post.h +++ b/include/asm-i386/mach-visws/setup_arch_post.h @@ -10,7 +10,7 @@ unsigned long sgivwfb_mem_size; long long mem_size __initdata = 0; -static inline char * __init machine_specific_memory_setup(void) +static char * __init machine_specific_memory_setup(void) { long long gfx_mem_size = 8 * MB; diff --git a/include/asm-i386/mach-voyager/setup_arch_post.h b/include/asm-i386/mach-voyager/setup_arch_post.h index 4c7cef49b8b2..f6f6c2cbc75c 100644 --- a/include/asm-i386/mach-voyager/setup_arch_post.h +++ b/include/asm-i386/mach-voyager/setup_arch_post.h @@ -3,7 +3,7 @@ * This is included late in kernel/setup.c so that it can make use of all of * the static functions. */ -static inline char * __init machine_specific_memory_setup(void) +static char * __init machine_specific_memory_setup(void) { char *who; -- cgit v1.2.3 From a2816bbfb17101d2faa45c8b557da257a98fe729 Mon Sep 17 00:00:00 2001 From: Dean Nelson Date: Sun, 20 Jun 2004 04:54:29 -0700 Subject: [PATCH] add wait_event_interruptible_exclusive() macro This patch defines a macro that does exactly what wait_event_interruptible() does except that it adds the current task to the wait queue as an exclusive task (i.e., sets the WQ_FLAG_EXCLUSIVE flag) rather than as a non-exclusive task as wait_event_interruptible() does. This allows one to do a wake_up_nr() to wake up a specific number of tasks. I'm in the process of submitting a patch to linux-ia64 that requires this capability. (Its subject line is "[PATCH 3/4] SGI Altix cross partition functionality".) Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/wait.h | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/wait.h b/include/linux/wait.h index 52edb1786b14..4a9f996bb6cc 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -200,7 +200,36 @@ do { \ __wait_event_interruptible_timeout(wq, condition, __ret); \ __ret; \ }) - + +#define __wait_event_interruptible_exclusive(wq, condition, ret) \ +do { \ + wait_queue_t __wait; \ + init_waitqueue_entry(&__wait, current); \ + \ + add_wait_queue_exclusive(&wq, &__wait); \ + for (;;) { \ + set_current_state(TASK_INTERRUPTIBLE); \ + if (condition) \ + break; \ + if (!signal_pending(current)) { \ + schedule(); \ + continue; \ + } \ + ret = -ERESTARTSYS; \ + break; \ + } \ + current->state = TASK_RUNNING; \ + remove_wait_queue(&wq, &__wait); \ +} while (0) + +#define wait_event_interruptible_exclusive(wq, condition) \ +({ \ + int __ret = 0; \ + if (!(condition)) \ + __wait_event_interruptible_exclusive(wq, condition, __ret);\ + __ret; \ +}) + /* * Must be called with the spinlock in the wait_queue_head_t held. */ -- cgit v1.2.3 From 2fed84384a0b084d78252aa14d6bfae03deb268f Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 20 Jun 2004 04:54:40 -0700 Subject: [PATCH] iommu max segment size This patch is from James, I've changed it slightly only. The problem is that some IOMMU implementations have a maximum limit to the size of the number of contiguously mappable pages (admittedly, this limit is mostly in the resource management algorithms rather than the IOMMUs themselves). This patch adds this concept to the bio layer via the parameter BIO_VMERGE_MAX_SIZE which architectures can define in asm/io.h (if undefined, we assume it to be infinite, which is current behaviour). While adding this, I noticed several places where bio was making incorrect assumptions about virtual mergeability (none of which was a bug: bio was overestimating rather than underestimating). - The worst offender was bio_add_page(), which seemed never to check for virtual mergeability - I also fixed blk_hw_contig_segments() not to check the QUEUE_CLUSTER flag, and not to check the phys segment boundary. In order to track the hw segment size across bios, I had to introduce two extra bio parameters: bi_hw_front_size and bi_hw_back_size which store the sizes of the front and back hw contiguous segments (and which will be equal if there's only one hw segment). When the bio is merged into a request, these fields are updated with the total hw contig size so they can always be used to assess if the merger would violate the BIO_VMERGE_MAX_SIZE parameter. Signed-Off-By: Jens Axboe Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/block/ll_rw_blk.c | 104 +++++++++++++++++++++++++++++++++++----------- fs/bio.c | 14 +++++-- include/linux/bio.h | 18 ++++++++ 3 files changed, 109 insertions(+), 27 deletions(-) (limited to 'include') diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 32881024ecfa..713f3ecb7f40 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -817,14 +817,14 @@ EXPORT_SYMBOL(blk_dump_rq_flags); void blk_recount_segments(request_queue_t *q, struct bio *bio) { struct bio_vec *bv, *bvprv = NULL; - int i, nr_phys_segs, nr_hw_segs, seg_size, cluster; + int i, nr_phys_segs, nr_hw_segs, seg_size, hw_seg_size, cluster; int high, highprv = 1; if (unlikely(!bio->bi_io_vec)) return; cluster = q->queue_flags & (1 << QUEUE_FLAG_CLUSTER); - seg_size = nr_phys_segs = nr_hw_segs = 0; + hw_seg_size = seg_size = nr_phys_segs = nr_hw_segs = 0; bio_for_each_segment(bv, bio, i) { /* * the trick here is making sure that a high page is never @@ -841,22 +841,35 @@ void blk_recount_segments(request_queue_t *q, struct bio *bio) goto new_segment; if (!BIOVEC_SEG_BOUNDARY(q, bvprv, bv)) goto new_segment; + if (BIOVEC_VIRT_OVERSIZE(hw_seg_size + bv->bv_len)) + goto new_hw_segment; seg_size += bv->bv_len; + hw_seg_size += bv->bv_len; bvprv = bv; continue; } new_segment: - if (!BIOVEC_VIRT_MERGEABLE(bvprv, bv)) + if (BIOVEC_VIRT_MERGEABLE(bvprv, bv) && + !BIOVEC_VIRT_OVERSIZE(hw_seg_size + bv->bv_len)) { + hw_seg_size += bv->bv_len; + } else { new_hw_segment: + if (hw_seg_size > bio->bi_hw_front_size) + bio->bi_hw_front_size = hw_seg_size; + hw_seg_size = BIOVEC_VIRT_START_SIZE(bv) + bv->bv_len; nr_hw_segs++; + } nr_phys_segs++; bvprv = bv; seg_size = bv->bv_len; highprv = high; } - + if (hw_seg_size > bio->bi_hw_back_size) + bio->bi_hw_back_size = hw_seg_size; + if (nr_hw_segs == 1 && hw_seg_size > bio->bi_hw_front_size) + bio->bi_hw_front_size = hw_seg_size; bio->bi_phys_segments = nr_phys_segs; bio->bi_hw_segments = nr_hw_segs; bio->bi_flags |= (1 << BIO_SEG_VALID); @@ -889,22 +902,17 @@ EXPORT_SYMBOL(blk_phys_contig_segment); int blk_hw_contig_segment(request_queue_t *q, struct bio *bio, struct bio *nxt) { - if (!(q->queue_flags & (1 << QUEUE_FLAG_CLUSTER))) - return 0; - - if (!BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt))) + if (unlikely(!bio_flagged(bio, BIO_SEG_VALID))) + blk_recount_segments(q, bio); + if (unlikely(!bio_flagged(nxt, BIO_SEG_VALID))) + blk_recount_segments(q, nxt); + if (!BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(nxt)) || + BIOVEC_VIRT_OVERSIZE(bio->bi_hw_front_size + bio->bi_hw_back_size)) return 0; if (bio->bi_size + nxt->bi_size > q->max_segment_size) return 0; - /* - * bio and nxt are contigous in memory, check if the queue allows - * these two to be merged into one - */ - if (BIO_SEG_BOUNDARY(q, bio, nxt)) - return 1; - - return 0; + return 1; } EXPORT_SYMBOL(blk_hw_contig_segment); @@ -1012,14 +1020,30 @@ static inline int ll_new_hw_segment(request_queue_t *q, static int ll_back_merge_fn(request_queue_t *q, struct request *req, struct bio *bio) { + int len; + if (req->nr_sectors + bio_sectors(bio) > q->max_sectors) { req->flags |= REQ_NOMERGE; q->last_merge = NULL; return 0; } - - if (BIOVEC_VIRT_MERGEABLE(__BVEC_END(req->biotail), __BVEC_START(bio))) - return ll_new_mergeable(q, req, bio); + if (unlikely(!bio_flagged(req->biotail, BIO_SEG_VALID))) + blk_recount_segments(q, req->biotail); + if (unlikely(!bio_flagged(bio, BIO_SEG_VALID))) + blk_recount_segments(q, bio); + len = req->biotail->bi_hw_back_size + bio->bi_hw_front_size; + if (BIOVEC_VIRT_MERGEABLE(__BVEC_END(req->biotail), __BVEC_START(bio)) && + !BIOVEC_VIRT_OVERSIZE(len)) { + int mergeable = ll_new_mergeable(q, req, bio); + + if (mergeable) { + if (req->nr_hw_segments == 1) + req->bio->bi_hw_front_size = len; + if (bio->bi_hw_segments == 1) + bio->bi_hw_back_size = len; + } + return mergeable; + } return ll_new_hw_segment(q, req, bio); } @@ -1027,14 +1051,30 @@ static int ll_back_merge_fn(request_queue_t *q, struct request *req, static int ll_front_merge_fn(request_queue_t *q, struct request *req, struct bio *bio) { + int len; + if (req->nr_sectors + bio_sectors(bio) > q->max_sectors) { req->flags |= REQ_NOMERGE; q->last_merge = NULL; return 0; } - - if (BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(req->bio))) - return ll_new_mergeable(q, req, bio); + len = bio->bi_hw_back_size + req->bio->bi_hw_front_size; + if (unlikely(!bio_flagged(bio, BIO_SEG_VALID))) + blk_recount_segments(q, bio); + if (unlikely(!bio_flagged(req->bio, BIO_SEG_VALID))) + blk_recount_segments(q, req->bio); + if (BIOVEC_VIRT_MERGEABLE(__BVEC_END(bio), __BVEC_START(req->bio)) && + !BIOVEC_VIRT_OVERSIZE(len)) { + int mergeable = ll_new_mergeable(q, req, bio); + + if (mergeable) { + if (bio->bi_hw_segments == 1) + bio->bi_hw_front_size = len; + if (req->nr_hw_segments == 1) + req->biotail->bi_hw_back_size = len; + } + return mergeable; + } return ll_new_hw_segment(q, req, bio); } @@ -1066,8 +1106,17 @@ static int ll_merge_requests_fn(request_queue_t *q, struct request *req, return 0; total_hw_segments = req->nr_hw_segments + next->nr_hw_segments; - if (blk_hw_contig_segment(q, req->biotail, next->bio)) + if (blk_hw_contig_segment(q, req->biotail, next->bio)) { + int len = req->biotail->bi_hw_back_size + next->bio->bi_hw_front_size; + /* + * propagate the combined length to the end of the requests + */ + if (req->nr_hw_segments == 1) + req->bio->bi_hw_front_size = len; + if (next->nr_hw_segments == 1) + next->biotail->bi_hw_back_size = len; total_hw_segments--; + } if (total_hw_segments > q->max_hw_segments) return 0; @@ -2532,7 +2581,7 @@ EXPORT_SYMBOL(process_that_request_first); void blk_recalc_rq_segments(struct request *rq) { - struct bio *bio; + struct bio *bio, *prevbio = NULL; int nr_phys_segs, nr_hw_segs; if (!rq->bio) @@ -2545,6 +2594,13 @@ void blk_recalc_rq_segments(struct request *rq) nr_phys_segs += bio_phys_segments(rq->q, bio); nr_hw_segs += bio_hw_segments(rq->q, bio); + if (prevbio) { + if (blk_phys_contig_segment(rq->q, prevbio, bio)) + nr_phys_segs--; + if (blk_hw_contig_segment(rq->q, prevbio, bio)) + nr_hw_segs--; + } + prevbio = bio; } rq->nr_phys_segments = nr_phys_segs; diff --git a/fs/bio.c b/fs/bio.c index 2d1ec65361a5..ac03005be2b0 100644 --- a/fs/bio.c +++ b/fs/bio.c @@ -116,6 +116,8 @@ inline void bio_init(struct bio *bio) bio->bi_idx = 0; bio->bi_phys_segments = 0; bio->bi_hw_segments = 0; + bio->bi_hw_front_size = 0; + bio->bi_hw_back_size = 0; bio->bi_size = 0; bio->bi_max_vecs = 0; bio->bi_end_io = NULL; @@ -304,14 +306,15 @@ static int __bio_add_page(request_queue_t *q, struct bio *bio, struct page * make this too complex. */ - while (bio_phys_segments(q, bio) >= q->max_phys_segments - || bio_hw_segments(q, bio) >= q->max_hw_segments) { + while (bio->bi_phys_segments >= q->max_phys_segments + || bio->bi_hw_segments >= q->max_hw_segments + || BIOVEC_VIRT_OVERSIZE(bio->bi_size)) { if (retried_segments) return 0; - bio->bi_flags &= ~(1 << BIO_SEG_VALID); retried_segments = 1; + blk_recount_segments(q, bio); } /* @@ -341,6 +344,11 @@ static int __bio_add_page(request_queue_t *q, struct bio *bio, struct page } } + /* If we may be able to merge these biovecs, force a recount */ + if (bio->bi_vcnt && (BIOVEC_PHYS_MERGEABLE(bvec-1, bvec) || + BIOVEC_VIRT_MERGEABLE(bvec-1, bvec))) + bio->bi_flags &= ~(1 << BIO_SEG_VALID); + bio->bi_vcnt++; bio->bi_phys_segments++; bio->bi_hw_segments++; diff --git a/include/linux/bio.h b/include/linux/bio.h index c4dd287dd1c8..601531cf4976 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -25,6 +25,15 @@ /* Platforms may set this to teach the BIO layer about IOMMU hardware. */ #include + +#if defined(BIO_VMERGE_MAX_SIZE) && defined(BIO_VMERGE_BOUNDARY) +#define BIOVEC_VIRT_START_SIZE(x) (bvec_to_phys(x) & (BIO_VMERGE_BOUNDARY - 1)) +#define BIOVEC_VIRT_OVERSIZE(x) ((x) > BIO_VMERGE_MAX_SIZE) +#else +#define BIOVEC_VIRT_START_SIZE(x) 0 +#define BIOVEC_VIRT_OVERSIZE(x) 0 +#endif + #ifndef BIO_VMERGE_BOUNDARY #define BIO_VMERGE_BOUNDARY 0 #endif @@ -81,6 +90,15 @@ struct bio { unsigned short bi_hw_segments; unsigned int bi_size; /* residual I/O count */ + + /* + * To keep track of the max hw size, we account for the + * sizes of the first and last virtually mergeable segments + * in this bio + */ + unsigned int bi_hw_front_size; + unsigned int bi_hw_back_size; + unsigned int bi_max_vecs; /* max bvl_vecs we can hold */ struct bio_vec *bi_io_vec; /* the actual vec list */ -- cgit v1.2.3 From cc843d2e8176bfb1f5d1f1065a58b70cd0f78690 Mon Sep 17 00:00:00 2001 From: Gerd Knorr Date: Sun, 20 Jun 2004 04:54:51 -0700 Subject: [PATCH] v4l: v4l2 API updates This patch has some minor updates to v4l2 API: * A new pixel format (V4L2_PIX_FMT_SBGGR8). * Adds some #defines for tv norms for convenience. * Allow to specify the video source to capture from on a per-frame basis. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/videodev2.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index a4ab8e826bbe..ab8727d92739 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -207,6 +207,9 @@ struct v4l2_pix_format #define V4L2_PIX_FMT_YYUV v4l2_fourcc('Y','Y','U','V') /* 16 YUV 4:2:2 */ #define V4L2_PIX_FMT_HI240 v4l2_fourcc('H','I','2','4') /* 8 8-bit color */ +/* see http://www.siliconimaging.com/RGB%20Bayer.htm */ +#define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B','A','8','1') /* 8 BGBG.. GRGR.. */ + /* compressed formats */ #define V4L2_PIX_FMT_MJPEG v4l2_fourcc('M','J','P','G') /* Motion-JPEG */ #define V4L2_PIX_FMT_JPEG v4l2_fourcc('J','P','E','G') /* JFIF JPEG */ @@ -383,8 +386,8 @@ struct v4l2_buffer unsigned long userptr; } m; __u32 length; - - __u32 reserved[2]; + __u32 input; + __u32 reserved; }; /* Flags for 'flags' field */ @@ -395,6 +398,7 @@ struct v4l2_buffer #define V4L2_BUF_FLAG_PFRAME 0x0010 /* Image is a P-frame */ #define V4L2_BUF_FLAG_BFRAME 0x0020 /* Image is a B-frame */ #define V4L2_BUF_FLAG_TIMECODE 0x0100 /* timecode field is valid */ +#define V4L2_BUF_FLAG_INPUT 0x0200 /* input field is valid */ /* * O V E R L A Y P R E V I E W @@ -526,12 +530,13 @@ typedef __u64 v4l2_std_id; V4L2_STD_PAL_I) #define V4L2_STD_NTSC (V4L2_STD_NTSC_M |\ V4L2_STD_NTSC_M_JP) +#define V4L2_STD_SECAM_DK (V4L2_STD_SECAM_D |\ + V4L2_STD_SECAM_K |\ + V4L2_STD_SECAM_K1) #define V4L2_STD_SECAM (V4L2_STD_SECAM_B |\ - V4L2_STD_SECAM_D |\ V4L2_STD_SECAM_G |\ V4L2_STD_SECAM_H |\ - V4L2_STD_SECAM_K |\ - V4L2_STD_SECAM_K1 |\ + V4L2_STD_SECAM_DK |\ V4L2_STD_SECAM_L) #define V4L2_STD_525_60 (V4L2_STD_PAL_M |\ @@ -541,6 +546,8 @@ typedef __u64 v4l2_std_id; V4L2_STD_PAL_N |\ V4L2_STD_PAL_Nc |\ V4L2_STD_SECAM) +#define V4L2_STD_ATSC (V4L2_STD_ATSC_8_VSB |\ + V4L2_STD_ATSC_16_VSB) #define V4L2_STD_UNKNOWN 0 #define V4L2_STD_ALL (V4L2_STD_525_60 |\ -- cgit v1.2.3 From e6f32f3995545a8bfe838a49433d8194e0508062 Mon Sep 17 00:00:00 2001 From: Gerd Knorr Date: Sun, 20 Jun 2004 04:55:02 -0700 Subject: [PATCH] v4l: update video-buf for per-frame input switching. This patch updates the video-buf module to support the per-frame input switching added by the v4l2 API patch. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/media/video/video-buf.c | 14 ++++++++++++++ include/media/video-buf.h | 6 +++++- 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/drivers/media/video/video-buf.c b/drivers/media/video/video-buf.c index f541a06df378..83db2e618ba7 100644 --- a/drivers/media/video/video-buf.c +++ b/drivers/media/video/video-buf.c @@ -491,6 +491,11 @@ videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb, break; } + if (vb->input != UNSET) { + b->flags |= V4L2_BUF_FLAG_INPUT; + b->input = vb->input; + } + b->field = vb->field; b->timestamp = vb->ts; b->bytesused = vb->size; @@ -574,6 +579,14 @@ videobuf_qbuf(struct file *file, struct videobuf_queue *q, buf->state == STATE_ACTIVE) goto done; + if (b->flags & V4L2_BUF_FLAG_INPUT) { + if (b->input >= q->inputs) + goto done; + buf->input = b->input; + } else { + buf->input = UNSET; + } + switch (b->memory) { case V4L2_MEMORY_MMAP: if (0 == buf->baddr) @@ -1075,6 +1088,7 @@ int videobuf_mmap_setup(struct file *file, struct videobuf_queue *q, for (i = 0; i < bcount; i++) { q->bufs[i] = videobuf_alloc(q->msize); q->bufs[i]->i = i; + q->bufs[i]->input = UNSET; q->bufs[i]->memory = memory; q->bufs[i]->bsize = bsize; switch (memory) { diff --git a/include/media/video-buf.h b/include/media/video-buf.h index 41e9e58c434e..02452be56b52 100644 --- a/include/media/video-buf.h +++ b/include/media/video-buf.h @@ -18,6 +18,8 @@ #include +#define UNSET (-1U) + /* --------------------------------------------------------------------- */ /* @@ -140,6 +142,7 @@ struct videobuf_buffer { unsigned int height; unsigned int bytesperline; /* use only if != 0 */ unsigned long size; + unsigned int input; enum v4l2_field field; enum videobuf_state state; struct videobuf_dmabuf dma; @@ -174,9 +177,10 @@ struct videobuf_queue { struct pci_dev *pci; enum v4l2_buf_type type; + unsigned int inputs; /* for V4L2_BUF_FLAG_INPUT */ unsigned int msize; enum v4l2_field field; - enum v4l2_field last; /* for field=V4L2_FIELD_ALTERNATE */ + enum v4l2_field last; /* for field=V4L2_FIELD_ALTERNATE */ struct videobuf_buffer *bufs[VIDEO_MAX_FRAME]; struct videobuf_queue_ops *ops; -- cgit v1.2.3 From 800c5a77c5e936ebf6d6759a4c700a122877ce9f Mon Sep 17 00:00:00 2001 From: Gerd Knorr Date: Sun, 20 Jun 2004 04:55:13 -0700 Subject: [PATCH] v4l: video-buf magic numbers This patch adds some magic IDs and checks for them to the data structs of the video-buf module. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/media/video/video-buf.c | 32 ++++++++++++++++++++++++++------ include/media/video-buf.h | 4 ++++ 2 files changed, 30 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/media/video/video-buf.c b/drivers/media/video/video-buf.c index 83db2e618ba7..0e8330b6f2fb 100644 --- a/drivers/media/video/video-buf.c +++ b/drivers/media/video/video-buf.c @@ -28,6 +28,11 @@ #include +#define MAGIC_DMABUF 0x19721112 +#define MAGIC_BUFFER 0x20040302 +#define MAGIC_CHECK(is,should) if (unlikely((is) != (should))) \ + { printk(KERN_ERR "magic mismatch: %x (expected %x)\n",is,should); BUG(); } + static int debug = 0; MODULE_DESCRIPTION("helper module to manage video4linux pci dma buffers"); @@ -109,6 +114,12 @@ videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset) /* --------------------------------------------------------------------- */ +void videobuf_dma_init(struct videobuf_dmabuf *dma) +{ + memset(dma,0,sizeof(*dma)); + dma->magic = MAGIC_DMABUF; +} + int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, unsigned long data, unsigned long size) { @@ -178,8 +189,8 @@ int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction, int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma) { - if (0 == dma->nr_pages) - BUG(); + MAGIC_CHECK(dma->magic,MAGIC_DMABUF); + BUG_ON(0 == dma->nr_pages); if (dma->pages) { dma->sglist = videobuf_pages_to_sg(dma->pages, dma->nr_pages, @@ -211,8 +222,8 @@ int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma) int videobuf_dma_pci_sync(struct pci_dev *dev, struct videobuf_dmabuf *dma) { - if (!dma->sglen) - BUG(); + MAGIC_CHECK(dma->magic,MAGIC_DMABUF); + BUG_ON(!dma->sglen); if (!dma->bus_addr) pci_dma_sync_sg_for_cpu(dev,dma->sglist,dma->nr_pages,dma->direction); @@ -221,6 +232,7 @@ int videobuf_dma_pci_sync(struct pci_dev *dev, struct videobuf_dmabuf *dma) int videobuf_dma_pci_unmap(struct pci_dev *dev, struct videobuf_dmabuf *dma) { + MAGIC_CHECK(dma->magic,MAGIC_DMABUF); if (!dma->sglen) return 0; @@ -234,8 +246,8 @@ int videobuf_dma_pci_unmap(struct pci_dev *dev, struct videobuf_dmabuf *dma) int videobuf_dma_free(struct videobuf_dmabuf *dma) { - if (dma->sglen) - BUG(); + MAGIC_CHECK(dma->magic,MAGIC_DMABUF); + BUG_ON(dma->sglen); if (dma->pages) { int i; @@ -264,7 +276,9 @@ void* videobuf_alloc(unsigned int size) vb = kmalloc(size,GFP_KERNEL); if (NULL != vb) { memset(vb,0,size); + videobuf_dma_init(&vb->dma); init_waitqueue_head(&vb->done); + vb->magic = MAGIC_BUFFER; } return vb; } @@ -274,6 +288,7 @@ int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr) int retval = 0; DECLARE_WAITQUEUE(wait, current); + MAGIC_CHECK(vb->magic,MAGIC_BUFFER); add_wait_queue(&vb->done, &wait); while (vb->state == STATE_ACTIVE || vb->state == STATE_QUEUED) { if (non_blocking) { @@ -302,6 +317,7 @@ videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb, int err,pages; dma_addr_t bus; + MAGIC_CHECK(vb->magic,MAGIC_BUFFER); switch (vb->memory) { case V4L2_MEMORY_MMAP: case V4L2_MEMORY_USERPTR: @@ -453,6 +469,8 @@ void videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb, enum v4l2_buf_type type) { + MAGIC_CHECK(vb->magic,MAGIC_BUFFER); + b->index = vb->i; b->type = type; @@ -573,6 +591,7 @@ videobuf_qbuf(struct file *file, struct videobuf_queue *q, buf = q->bufs[b->index]; if (NULL == buf) goto done; + MAGIC_CHECK(buf->magic,MAGIC_BUFFER); if (buf->memory != b->memory) goto done; if (buf->state == STATE_QUEUED || @@ -1206,6 +1225,7 @@ int videobuf_mmap_mapper(struct vm_area_struct *vma, EXPORT_SYMBOL_GPL(videobuf_vmalloc_to_sg); +EXPORT_SYMBOL_GPL(videobuf_dma_init); EXPORT_SYMBOL_GPL(videobuf_dma_init_user); EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay); diff --git a/include/media/video-buf.h b/include/media/video-buf.h index 02452be56b52..c3a647aef99d 100644 --- a/include/media/video-buf.h +++ b/include/media/video-buf.h @@ -61,6 +61,8 @@ int videobuf_unlock(struct page **pages, int nr_pages); */ struct videobuf_dmabuf { + u32 magic; + /* for userland buffer */ int offset; struct page **pages; @@ -78,6 +80,7 @@ struct videobuf_dmabuf { int direction; }; +void videobuf_dma_init(struct videobuf_dmabuf *dma); int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, unsigned long data, unsigned long size); int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, @@ -136,6 +139,7 @@ enum videobuf_state { struct videobuf_buffer { unsigned int i; + u32 magic; /* info about the buffer */ unsigned int width; -- cgit v1.2.3 From 8879ee9f0ccdba3cec75caa710a08b6d4bc6c009 Mon Sep 17 00:00:00 2001 From: Gerd Knorr Date: Sun, 20 Jun 2004 04:55:24 -0700 Subject: [PATCH] v4l: video-buf fixes. This patch has some minor bugfixes for the video-buf module. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/media/video/video-buf.c | 15 +++++++++++---- include/media/video-buf.h | 2 -- 2 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/drivers/media/video/video-buf.c b/drivers/media/video/video-buf.c index 0e8330b6f2fb..6b2eee6de4dc 100644 --- a/drivers/media/video/video-buf.c +++ b/drivers/media/video/video-buf.c @@ -5,10 +5,10 @@ * The functions expect the hardware being able to scatter gatter * (i.e. the buffers are not linear in physical memory, but fragmented * into PAGE_SIZE chunks). They also assume the driver does not need - * to touch the video data (thus it is probably not useful for USB as - * data often must be uncompressed by the drivers). + * to touch the video data (thus it is probably not useful for USB 1.1 + * as data often must be uncompressed by the drivers). * - * (c) 2001,02 Gerd Knorr + * (c) 2001-2004 Gerd Knorr [SUSE Labs] * * 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 @@ -536,6 +536,11 @@ videobuf_reqbufs(struct file *file, struct videobuf_queue *q, req->memory != V4L2_MEMORY_OVERLAY) return -EINVAL; + if (q->streaming) + return -EBUSY; + if (!list_empty(&q->stream)) + return -EBUSY; + down(&q->lock); count = req->count; if (count > VIDEO_MAX_FRAME) @@ -614,6 +619,8 @@ videobuf_qbuf(struct file *file, struct videobuf_queue *q, case V4L2_MEMORY_USERPTR: if (b->length < buf->bsize) goto done; + if (STATE_NEEDS_INIT != buf->state && buf->baddr != b->m.userptr) + q->ops->buf_release(file,buf); buf->baddr = b->m.userptr; break; case V4L2_MEMORY_OVERLAY: @@ -1118,7 +1125,7 @@ int videobuf_mmap_setup(struct file *file, struct videobuf_queue *q, case V4L2_MEMORY_OVERLAY: /* nothing */ break; - }; + } } dprintk(1,"mmap setup: %d buffers, %d bytes each\n", bcount,bsize); diff --git a/include/media/video-buf.h b/include/media/video-buf.h index c3a647aef99d..17b2ae290f13 100644 --- a/include/media/video-buf.h +++ b/include/media/video-buf.h @@ -36,8 +36,6 @@ struct scatterlist* videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages); */ struct scatterlist* videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset); -int videobuf_lock(struct page **pages, int nr_pages); -int videobuf_unlock(struct page **pages, int nr_pages); /* --------------------------------------------------------------------- */ -- cgit v1.2.3 From 39871f0b772022388242ff07b5c3461f9d03b472 Mon Sep 17 00:00:00 2001 From: Gerd Knorr Date: Sun, 20 Jun 2004 04:55:46 -0700 Subject: [PATCH] v4l: ir-common update Some minor changes for the ir-common module: Update for the RC5 keytable and increase the IR_KEYTAB_SIZE #define. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/media/common/ir-common.c | 4 ++-- include/media/ir-common.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/drivers/media/common/ir-common.c b/drivers/media/common/ir-common.c index 7e92160519e2..2c007571a03e 100644 --- a/drivers/media/common/ir-common.c +++ b/drivers/media/common/ir-common.c @@ -75,6 +75,8 @@ IR_KEYTAB_TYPE ir_codes_rc5_tv[IR_KEYTAB_SIZE] = { [ 0x35 ] = KEY_PLAY, // play [ 0x36 ] = KEY_STOP, // stop [ 0x37 ] = KEY_RECORD, // recording + [ 0x3c ] = KEY_TEXT, // teletext submode (Japan: 12) + [ 0x3d ] = KEY_SUSPEND, // system standby #if 0 /* FIXME */ [ 0x0a ] = KEY_RESERVED, // 1/2/3 digits (japan: 10) @@ -106,8 +108,6 @@ IR_KEYTAB_TYPE ir_codes_rc5_tv[IR_KEYTAB_SIZE] = { [ 0x39 ] = KEY_RESERVED, // external 2 [ 0x3a ] = KEY_RESERVED, // PIP display mode [ 0x3b ] = KEY_RESERVED, // view data mode / advance - [ 0x3c ] = KEY_RESERVED, // teletext submode (Japan: 12) - [ 0x3d ] = KEY_RESERVED, // system standby [ 0x3e ] = KEY_RESERVED, // crispener on/off [ 0x3f ] = KEY_RESERVED, // system select #endif diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 42206f88ff25..569f74839529 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -27,7 +27,7 @@ #define IR_TYPE_OTHER 99 #define IR_KEYTAB_TYPE u32 -#define IR_KEYTAB_SIZE 64 // enougth for rc5, probably need more some day ... +#define IR_KEYTAB_SIZE 128 // enougth for rc5, probably need more some day ... #define IR_KEYCODE(tab,code) (((unsigned)code < IR_KEYTAB_SIZE) \ ? tab[code] : KEY_RESERVED) -- cgit v1.2.3 From 5d119c3c357cfea3d8ab2c23529f0d1da3369b6a Mon Sep 17 00:00:00 2001 From: Ralf Bächle Date: Sun, 20 Jun 2004 06:44:45 -0700 Subject: [PATCH] DS1286 cleanups Remove #ifdef'ed hooks for the DS1286 driver through the kernel. While cleaning make it work as a module also and add back the core of the driver got lost when drivers/sgi/ was shredded. Signed-off-by: Ralf Baechle Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- arch/mips/configs/ip22_defconfig | 2 +- arch/mips/defconfig | 2 +- drivers/char/Kconfig | 11 + drivers/char/Makefile | 1 + drivers/char/ds1286.c | 578 +++++++++++++++++++++++++++++++++++++++ drivers/char/misc.c | 4 - fs/proc/proc_misc.c | 15 - include/linux/ds1286.h | 54 ++++ 8 files changed, 646 insertions(+), 21 deletions(-) create mode 100644 drivers/char/ds1286.c create mode 100644 include/linux/ds1286.h (limited to 'include') diff --git a/arch/mips/configs/ip22_defconfig b/arch/mips/configs/ip22_defconfig index bbd6215cc611..912beee0c66c 100644 --- a/arch/mips/configs/ip22_defconfig +++ b/arch/mips/configs/ip22_defconfig @@ -583,7 +583,7 @@ CONFIG_WATCHDOG=y CONFIG_INDYDOG=m # CONFIG_RTC is not set # CONFIG_GEN_RTC is not set -CONFIG_SGI_DS1286=y +CONFIG_SGI_DS1286=m # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set diff --git a/arch/mips/defconfig b/arch/mips/defconfig index bbd6215cc611..912beee0c66c 100644 --- a/arch/mips/defconfig +++ b/arch/mips/defconfig @@ -583,7 +583,7 @@ CONFIG_WATCHDOG=y CONFIG_INDYDOG=m # CONFIG_RTC is not set # CONFIG_GEN_RTC is not set -CONFIG_SGI_DS1286=y +CONFIG_SGI_DS1286=m # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 2fe8bfab039e..c286a5e42159 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -753,6 +753,17 @@ config RTC To compile this driver as a module, choose M here: the module will be called rtc. +config SGI_DS1286 + tristate "SGI DS1286 RTC support" + depends on SGI_IP22 + help + If you say Y here and create a character special file /dev/rtc with + major number 10 and minor number 135 using mknod ("man mknod"), you + will get access to the real time clock built into your computer. + Every SGI has such a clock built in. It reports status information + via the file /proc/rtc and its behaviour is set by various ioctls on + /dev/rtc. + config GEN_RTC tristate "Generic /dev/rtc emulation" depends on RTC!=y && !IA64 diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 789890c8c82b..893975d29374 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_RTC) += rtc.o obj-$(CONFIG_HPET) += hpet.o obj-$(CONFIG_GEN_RTC) += genrtc.o obj-$(CONFIG_EFI_RTC) += efirtc.o +obj-$(CONFIG_SGI_DS1286) += ds1286.o ifeq ($(CONFIG_GENERIC_NVRAM),y) obj-$(CONFIG_NVRAM) += generic_nvram.o else diff --git a/drivers/char/ds1286.c b/drivers/char/ds1286.c new file mode 100644 index 000000000000..bc042fb2d5cd --- /dev/null +++ b/drivers/char/ds1286.c @@ -0,0 +1,578 @@ +/* + * DS1286 Real Time Clock interface for Linux + * + * Copyright (C) 1998, 1999, 2000 Ralf Baechle + * + * Based on code written by Paul Gortmaker. + * + * This driver allows use of the real time clock (built into nearly all + * computers) from user space. It exports the /dev/rtc interface supporting + * various ioctl() and also the /proc/rtc pseudo-file for status + * information. + * + * The ioctls can be used to set the interrupt behaviour and generation rate + * from the RTC via IRQ 8. Then the /dev/rtc interface can be used to make + * use of these timer interrupts, be they interval or alarm based. + * + * The /dev/rtc interface will block on reads until an interrupt has been + * received. If a RTC interrupt has already happened, it will output an + * unsigned long and then block. The output value contains the interrupt + * status in the low byte and the number of interrupts since the last read + * in the remaining high bytes. The /dev/rtc interface can also be used with + * the select(2) call. + * + * 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 +#include +#include +#include + +#include +#include + +#define DS1286_VERSION "1.0" + +/* + * We sponge a minor off of the misc major. No need slurping + * up another valuable major dev number for this. If you add + * an ioctl, make sure you don't conflict with SPARC's RTC + * ioctls. + */ + +static DECLARE_WAIT_QUEUE_HEAD(ds1286_wait); + +static ssize_t ds1286_read(struct file *file, char *buf, + size_t count, loff_t *ppos); + +static int ds1286_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +static unsigned int ds1286_poll(struct file *file, poll_table *wait); + +static void ds1286_get_alm_time (struct rtc_time *alm_tm); +static void ds1286_get_time(struct rtc_time *rtc_tm); +static int ds1286_set_time(struct rtc_time *rtc_tm); + +static inline unsigned char ds1286_is_updating(void); + +static spinlock_t ds1286_lock = SPIN_LOCK_UNLOCKED; + +static int ds1286_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); + +/* + * Bits in rtc_status. (7 bits of room for future expansion) + */ + +#define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */ +#define RTC_TIMER_ON 0x02 /* missed irq timer active */ + +static unsigned char ds1286_status; /* bitmapped status byte. */ + +static unsigned char days_in_mo[] = { + 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +/* + * Now all the various file operations that we export. + */ + +static ssize_t ds1286_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + return -EIO; +} + +static int ds1286_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct rtc_time wtime; + + switch (cmd) { + case RTC_AIE_OFF: /* Mask alarm int. enab. bit */ + { + unsigned int flags; + unsigned char val; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + spin_lock_irqsave(&ds1286_lock, flags); + val = rtc_read(RTC_CMD); + val |= RTC_TDM; + rtc_write(val, RTC_CMD); + spin_unlock_irqrestore(&ds1286_lock, flags); + + return 0; + } + case RTC_AIE_ON: /* Allow alarm interrupts. */ + { + unsigned int flags; + unsigned char val; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + spin_lock_irqsave(&ds1286_lock, flags); + val = rtc_read(RTC_CMD); + val &= ~RTC_TDM; + rtc_write(val, RTC_CMD); + spin_unlock_irqrestore(&ds1286_lock, flags); + + return 0; + } + case RTC_WIE_OFF: /* Mask watchdog int. enab. bit */ + { + unsigned int flags; + unsigned char val; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + spin_lock_irqsave(&ds1286_lock, flags); + val = rtc_read(RTC_CMD); + val |= RTC_WAM; + rtc_write(val, RTC_CMD); + spin_unlock_irqrestore(&ds1286_lock, flags); + + return 0; + } + case RTC_WIE_ON: /* Allow watchdog interrupts. */ + { + unsigned int flags; + unsigned char val; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + spin_lock_irqsave(&ds1286_lock, flags); + val = rtc_read(RTC_CMD); + val &= ~RTC_WAM; + rtc_write(val, RTC_CMD); + spin_unlock_irqrestore(&ds1286_lock, flags); + + return 0; + } + case RTC_ALM_READ: /* Read the present alarm time */ + { + /* + * This returns a struct rtc_time. Reading >= 0xc0 + * means "don't care" or "match all". Only the tm_hour, + * tm_min, and tm_sec values are filled in. + */ + + memset(&wtime, 0, sizeof(wtime)); + ds1286_get_alm_time(&wtime); + break; + } + case RTC_ALM_SET: /* Store a time into the alarm */ + { + /* + * This expects a struct rtc_time. Writing 0xff means + * "don't care" or "match all". Only the tm_hour, + * tm_min and tm_sec are used. + */ + unsigned char hrs, min, sec; + struct rtc_time alm_tm; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&alm_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + + hrs = alm_tm.tm_hour; + min = alm_tm.tm_min; + + if (hrs >= 24) + hrs = 0xff; + + if (min >= 60) + min = 0xff; + + BIN_TO_BCD(sec); + BIN_TO_BCD(min); + BIN_TO_BCD(hrs); + + spin_lock(&ds1286_lock); + rtc_write(hrs, RTC_HOURS_ALARM); + rtc_write(min, RTC_MINUTES_ALARM); + spin_unlock(&ds1286_lock); + + return 0; + } + case RTC_RD_TIME: /* Read the time/date from RTC */ + { + memset(&wtime, 0, sizeof(wtime)); + ds1286_get_time(&wtime); + break; + } + case RTC_SET_TIME: /* Set the RTC */ + { + struct rtc_time rtc_tm; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, + sizeof(struct rtc_time))) + return -EFAULT; + + return ds1286_set_time(&rtc_tm); + } + default: + return -EINVAL; + } + return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0; +} + +/* + * We enforce only one user at a time here with the open/close. + * Also clear the previous interrupt data on an open, and clean + * up things on a close. + */ + +static int ds1286_open(struct inode *inode, struct file *file) +{ + spin_lock_irq(&ds1286_lock); + + if (ds1286_status & RTC_IS_OPEN) + goto out_busy; + + ds1286_status |= RTC_IS_OPEN; + + spin_unlock_irq(&ds1286_lock); + return 0; + +out_busy: + spin_lock_irq(&ds1286_lock); + return -EBUSY; +} + +static int ds1286_release(struct inode *inode, struct file *file) +{ + ds1286_status &= ~RTC_IS_OPEN; + + return 0; +} + +static unsigned int ds1286_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &ds1286_wait, wait); + + return 0; +} + +/* + * The various file operations we support. + */ + +static struct file_operations ds1286_fops = { + .llseek = no_llseek, + .read = ds1286_read, + .poll = ds1286_poll, + .ioctl = ds1286_ioctl, + .open = ds1286_open, + .release = ds1286_release, +}; + +static struct miscdevice ds1286_dev= +{ + .minor = RTC_MINOR, + .name = "rtc", + .fops = &ds1286_fops, +}; + +static int __init ds1286_init(void) +{ + int err; + + printk(KERN_INFO "DS1286 Real Time Clock Driver v%s\n", DS1286_VERSION); + + err = misc_register(&ds1286_dev); + if (err) + goto out; + + if (!create_proc_read_entry("driver/rtc", 0, 0, ds1286_read_proc, NULL)) { + err = -ENOMEM; + + goto out_deregister; + } + + return 0; + +out_deregister: + misc_deregister(&ds1286_dev); + +out: + return err; +} + +static void __exit ds1286_exit(void) +{ + remove_proc_entry("driver/rtc", NULL); + misc_deregister(&ds1286_dev); +} + +static char *days[] = { + "***", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +/* + * Info exported via "/proc/rtc". + */ +static int ds1286_proc_output(char *buf) +{ + char *p, *s; + struct rtc_time tm; + unsigned char hundredth, month, cmd, amode; + + p = buf; + + ds1286_get_time(&tm); + hundredth = rtc_read(RTC_HUNDREDTH_SECOND); + BCD_TO_BIN(hundredth); + + p += sprintf(p, + "rtc_time\t: %02d:%02d:%02d.%02d\n" + "rtc_date\t: %04d-%02d-%02d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, hundredth, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + + /* + * We implicitly assume 24hr mode here. Alarm values >= 0xc0 will + * match any value for that particular field. Values that are + * greater than a valid time, but less than 0xc0 shouldn't appear. + */ + ds1286_get_alm_time(&tm); + p += sprintf(p, "alarm\t\t: %s ", days[tm.tm_wday]); + if (tm.tm_hour <= 24) + p += sprintf(p, "%02d:", tm.tm_hour); + else + p += sprintf(p, "**:"); + + if (tm.tm_min <= 59) + p += sprintf(p, "%02d\n", tm.tm_min); + else + p += sprintf(p, "**\n"); + + month = rtc_read(RTC_MONTH); + p += sprintf(p, + "oscillator\t: %s\n" + "square_wave\t: %s\n", + (month & RTC_EOSC) ? "disabled" : "enabled", + (month & RTC_ESQW) ? "disabled" : "enabled"); + + amode = ((rtc_read(RTC_MINUTES_ALARM) & 0x80) >> 5) | + ((rtc_read(RTC_HOURS_ALARM) & 0x80) >> 6) | + ((rtc_read(RTC_DAY_ALARM) & 0x80) >> 7); + if (amode == 7) s = "each minute"; + else if (amode == 3) s = "minutes match"; + else if (amode == 1) s = "hours and minutes match"; + else if (amode == 0) s = "days, hours and minutes match"; + else s = "invalid"; + p += sprintf(p, "alarm_mode\t: %s\n", s); + + cmd = rtc_read(RTC_CMD); + p += sprintf(p, + "alarm_enable\t: %s\n" + "wdog_alarm\t: %s\n" + "alarm_mask\t: %s\n" + "wdog_alarm_mask\t: %s\n" + "interrupt_mode\t: %s\n" + "INTB_mode\t: %s_active\n" + "interrupt_pins\t: %s\n", + (cmd & RTC_TDF) ? "yes" : "no", + (cmd & RTC_WAF) ? "yes" : "no", + (cmd & RTC_TDM) ? "disabled" : "enabled", + (cmd & RTC_WAM) ? "disabled" : "enabled", + (cmd & RTC_PU_LVL) ? "pulse" : "level", + (cmd & RTC_IBH_LO) ? "low" : "high", + (cmd & RTC_IPSW) ? "unswapped" : "swapped"); + + return p - buf; +} + +static int ds1286_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = ds1286_proc_output (page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) + len = count; + if (len<0) + len = 0; + + return len; +} + +/* + * Returns true if a clock update is in progress + */ +static inline unsigned char ds1286_is_updating(void) +{ + return rtc_read(RTC_CMD) & RTC_TE; +} + + +static void ds1286_get_time(struct rtc_time *rtc_tm) +{ + unsigned char save_control; + unsigned int flags; + unsigned long uip_watchdog = jiffies; + + /* + * read RTC once any update in progress is done. The update + * can take just over 2ms. We wait 10 to 20ms. There is no need to + * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP. + * If you need to know *exactly* when a second has started, enable + * periodic update complete interrupts, (via ioctl) and then + * immediately read /dev/rtc which will block until you get the IRQ. + * Once the read clears, read the RTC time (again via ioctl). Easy. + */ + + if (ds1286_is_updating() != 0) + while (jiffies - uip_watchdog < 2*HZ/100) + barrier(); + + /* + * Only the values that we read from the RTC are set. We leave + * tm_wday, tm_yday and tm_isdst untouched. Even though the + * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated + * by the RTC when initially set to a non-zero value. + */ + spin_lock_irqsave(&ds1286_lock, flags); + save_control = rtc_read(RTC_CMD); + rtc_write((save_control|RTC_TE), RTC_CMD); + + rtc_tm->tm_sec = rtc_read(RTC_SECONDS); + rtc_tm->tm_min = rtc_read(RTC_MINUTES); + rtc_tm->tm_hour = rtc_read(RTC_HOURS) & 0x3f; + rtc_tm->tm_mday = rtc_read(RTC_DATE); + rtc_tm->tm_mon = rtc_read(RTC_MONTH) & 0x1f; + rtc_tm->tm_year = rtc_read(RTC_YEAR); + + rtc_write(save_control, RTC_CMD); + spin_unlock_irqrestore(&ds1286_lock, flags); + + BCD_TO_BIN(rtc_tm->tm_sec); + BCD_TO_BIN(rtc_tm->tm_min); + BCD_TO_BIN(rtc_tm->tm_hour); + BCD_TO_BIN(rtc_tm->tm_mday); + BCD_TO_BIN(rtc_tm->tm_mon); + BCD_TO_BIN(rtc_tm->tm_year); + + /* + * Account for differences between how the RTC uses the values + * and how they are defined in a struct rtc_time; + */ + if (rtc_tm->tm_year < 45) + rtc_tm->tm_year += 30; + if ((rtc_tm->tm_year += 40) < 70) + rtc_tm->tm_year += 100; + + rtc_tm->tm_mon--; +} + +static int ds1286_set_time(struct rtc_time *rtc_tm) +{ + unsigned char mon, day, hrs, min, sec, leap_yr; + unsigned char save_control; + unsigned int yrs, flags; + + + yrs = rtc_tm->tm_year + 1900; + mon = rtc_tm->tm_mon + 1; /* tm_mon starts at zero */ + day = rtc_tm->tm_mday; + hrs = rtc_tm->tm_hour; + min = rtc_tm->tm_min; + sec = rtc_tm->tm_sec; + + if (yrs < 1970) + return -EINVAL; + + leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); + + if ((mon > 12) || (day == 0)) + return -EINVAL; + + if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) + return -EINVAL; + + if ((hrs >= 24) || (min >= 60) || (sec >= 60)) + return -EINVAL; + + if ((yrs -= 1940) > 255) /* They are unsigned */ + return -EINVAL; + + if (yrs >= 100) + yrs -= 100; + + BIN_TO_BCD(sec); + BIN_TO_BCD(min); + BIN_TO_BCD(hrs); + BIN_TO_BCD(day); + BIN_TO_BCD(mon); + BIN_TO_BCD(yrs); + + spin_lock_irqsave(&ds1286_lock, flags); + save_control = rtc_read(RTC_CMD); + rtc_write((save_control|RTC_TE), RTC_CMD); + + rtc_write(yrs, RTC_YEAR); + rtc_write(mon, RTC_MONTH); + rtc_write(day, RTC_DATE); + rtc_write(hrs, RTC_HOURS); + rtc_write(min, RTC_MINUTES); + rtc_write(sec, RTC_SECONDS); + rtc_write(0, RTC_HUNDREDTH_SECOND); + + rtc_write(save_control, RTC_CMD); + spin_unlock_irqrestore(&ds1286_lock, flags); + + return 0; +} + +static void ds1286_get_alm_time(struct rtc_time *alm_tm) +{ + unsigned char cmd; + unsigned int flags; + + /* + * Only the values that we read from the RTC are set. That + * means only tm_wday, tm_hour, tm_min. + */ + spin_lock_irqsave(&ds1286_lock, flags); + alm_tm->tm_min = rtc_read(RTC_MINUTES_ALARM) & 0x7f; + alm_tm->tm_hour = rtc_read(RTC_HOURS_ALARM) & 0x1f; + alm_tm->tm_wday = rtc_read(RTC_DAY_ALARM) & 0x07; + cmd = rtc_read(RTC_CMD); + spin_unlock_irqrestore(&ds1286_lock, flags); + + BCD_TO_BIN(alm_tm->tm_min); + BCD_TO_BIN(alm_tm->tm_hour); + alm_tm->tm_sec = 0; +} + +module_init(ds1286_init); +module_exit(ds1286_exit); + +MODULE_AUTHOR("Ralf Baechle"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(RTC_MINOR); diff --git a/drivers/char/misc.c b/drivers/char/misc.c index 1bccc0d19207..13b11270a9f2 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -65,7 +65,6 @@ static unsigned char misc_minors[DYNAMIC_MINORS / 8]; extern int rtc_DP8570A_init(void); extern int rtc_MK48T08_init(void); -extern int ds1286_init(void); extern int pmu_device_init(void); extern int tosh_init(void); extern int i8k_init(void); @@ -314,9 +313,6 @@ static int __init misc_init(void) #ifdef CONFIG_BVME6000 rtc_DP8570A_init(); #endif -#ifdef CONFIG_SGI_DS1286 - ds1286_init(); -#endif #ifdef CONFIG_PMAC_PBOOK pmu_device_init(); #endif diff --git a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c index 2066d2551c9f..a46bd705e93b 100644 --- a/fs/proc/proc_misc.c +++ b/fs/proc/proc_misc.c @@ -66,9 +66,6 @@ extern int get_filesystem_list(char *); extern int get_exec_domain_list(char *); extern int get_dma_list(char *); extern int get_locks_status (char *, char **, off_t, int); -#ifdef CONFIG_SGI_DS1286 -extern int get_ds1286_status(char *); -#endif static int proc_calc_metrics(char *page, char **start, off_t off, int count, int *eof, int len) @@ -528,15 +525,6 @@ static int cmdline_read_proc(char *page, char **start, off_t off, return proc_calc_metrics(page, start, off, count, eof, len); } -#ifdef CONFIG_SGI_DS1286 -static int ds1286_read_proc(char *page, char **start, off_t off, - int count, int *eof, void *data) -{ - int len = get_ds1286_status(page); - return proc_calc_metrics(page, start, off, count, eof, len); -} -#endif - static int locks_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -670,9 +658,6 @@ void __init proc_misc_init(void) {"devices", devices_read_proc}, {"filesystems", filesystems_read_proc}, {"cmdline", cmdline_read_proc}, -#ifdef CONFIG_SGI_DS1286 - {"rtc", ds1286_read_proc}, -#endif {"locks", locks_read_proc}, {"execdomains", execdomains_read_proc}, {NULL,} diff --git a/include/linux/ds1286.h b/include/linux/ds1286.h new file mode 100644 index 000000000000..d8989860e4ce --- /dev/null +++ b/include/linux/ds1286.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 1998, 1999, 2003 Ralf Baechle + * + * 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. + */ +#ifndef __LINUX_DS1286_H +#define __LINUX_DS1286_H + +#include + +/********************************************************************** + * register summary + **********************************************************************/ +#define RTC_HUNDREDTH_SECOND 0 +#define RTC_SECONDS 1 +#define RTC_MINUTES 2 +#define RTC_MINUTES_ALARM 3 +#define RTC_HOURS 4 +#define RTC_HOURS_ALARM 5 +#define RTC_DAY 6 +#define RTC_DAY_ALARM 7 +#define RTC_DATE 8 +#define RTC_MONTH 9 +#define RTC_YEAR 10 +#define RTC_CMD 11 +#define RTC_WHSEC 12 +#define RTC_WSEC 13 +#define RTC_UNUSED 14 + +/* RTC_*_alarm is always true if 2 MSBs are set */ +# define RTC_ALARM_DONT_CARE 0xC0 + + +/* + * Bits in the month register + */ +#define RTC_EOSC 0x80 +#define RTC_ESQW 0x40 + +/* + * Bits in the Command register + */ +#define RTC_TDF 0x01 +#define RTC_WAF 0x02 +#define RTC_TDM 0x04 +#define RTC_WAM 0x08 +#define RTC_PU_LVL 0x10 +#define RTC_IBH_LO 0x20 +#define RTC_IPSW 0x40 +#define RTC_TE 0x80 + +#endif /* __LINUX_DS1286_H */ -- cgit v1.2.3 From 914d17491cfd3019e00b9f3271815da3815e6b56 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 20 Jun 2004 06:45:52 -0700 Subject: [PATCH] Fix idr.h comment Fix path in header file. Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- include/linux/idr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/linux/idr.h b/include/linux/idr.h index 8e2618e3f334..f41128683d65 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -1,5 +1,5 @@ /* - * include/linux/id.h + * include/linux/idr.h * * 2002-10-18 written by Jim Houston jim.houston@ccur.com * Copyright (C) 2002 by Concurrent Computer Corporation -- cgit v1.2.3