From: Pete P. <pp...@us...> - 2002-04-12 22:58:31
|
Update of /cvsroot/linux-mips/linux/drivers/video In directory usw-pr-cvs1:/tmp/cvs-serv29502/drivers/video Modified Files: epson1356fb.c Log Message: Current linux generic remap_page_range does not support higher than 32 bit physical addresses on a 32 bit kernel. In order to use the epson driver with a 36 bit MIPS32 chip select, I had to copy a few of the remap_page_range routines and modify them accorindly, in my local copy. This was tested on the Alchemy MIPS32 part which supports 36 bit phys addr. Index: epson1356fb.c =================================================================== RCS file: /cvsroot/linux-mips/linux/drivers/video/epson1356fb.c,v retrieving revision 1.8 retrieving revision 1.9 diff -u -d -r1.8 -r1.9 --- epson1356fb.c 7 Mar 2002 23:30:22 -0000 1.8 +++ epson1356fb.c 12 Apr 2002 22:58:28 -0000 1.9 @@ -42,7 +42,7 @@ #include <linux/string.h> #include <linux/mm.h> #include <linux/tty.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/delay.h> #include <linux/interrupt.h> @@ -57,6 +57,11 @@ #include <asm/io.h> #include <asm/uaccess.h> #include <linux/timer.h> +#include <linux/pagemap.h> + +#include <asm/pgalloc.h> +#include <asm/uaccess.h> +#include <asm/tlb.h> #ifdef CONFIG_MTRR #include <asm/mtrr.h> @@ -210,6 +215,8 @@ static struct e1356fb_fix boot_fix; // boot options static struct e1356fb_par boot_par; // boot options +static int e1356_remap_page_range(unsigned long from, phys_t phys_addr, unsigned long size, pgprot_t prot); + /* ------------------------------------------------------------------------- * Hardware-specific funcions @@ -2071,7 +2078,11 @@ { struct fb_info_e1356 *info = (struct fb_info_e1356*)fb; unsigned int len; +#if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32) + u64 start=0, off; +#else unsigned long start=0, off; +#endif if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) { DPRINTK("invalid vma->vm_pgoff\n"); @@ -2152,10 +2163,17 @@ if (info->fix.mmunalign) vma->vm_start += 2; +#if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32) + if (e1356_remap_page_range(vma->vm_start, off, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; +#else if (io_remap_page_range(vma->vm_start, off, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; +#endif info->mmaped = 1; return 0; @@ -3042,3 +3060,99 @@ printk("e1356fb: reserving 1024 bytes for the hwcursor at %p\n", fb_info.membase_virt + fb_info.fb_size); } + +#if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32) + +/* + * Return indicates whether a page was freed so caller can adjust rss + */ +static inline void forget_pte(pte_t page) +{ + if (!pte_none(page)) { + printk("forget_pte: old mapping existed!\n"); + BUG(); + } +} + +/* + * maps a range of physical memory into the requested pages. the old + * mappings are removed. any references to nonexistent pages results + * in null mappings (currently treated as "copy-on-access") + */ +static inline void e1356_remap_pte_range(pte_t * pte, unsigned long address, unsigned long size, + phys_t phys_addr, pgprot_t prot) +{ + unsigned long end; + + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + do { + struct page *page; + pte_t oldpage; + oldpage = ptep_get_and_clear(pte); + + page = virt_to_page(__va(phys_addr)); + if ((!VALID_PAGE(page)) || PageReserved(page)) + set_pte(pte, mk_pte_phys(phys_addr, prot)); + forget_pte(oldpage); + address += PAGE_SIZE; + phys_addr += PAGE_SIZE; + pte++; + } while (address && (address < end)); +} + +static inline int e1356_remap_pmd_range(struct mm_struct *mm, pmd_t * pmd, unsigned long address, unsigned long size, + phys_t phys_addr, pgprot_t prot) +{ + unsigned long end; + + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + phys_addr -= address; + do { + pte_t * pte = pte_alloc(mm, pmd, address); + if (!pte) + return -ENOMEM; + e1356_remap_pte_range(pte, address, end - address, address + phys_addr, prot); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address && (address < end)); + return 0; +} + +/* Note: this is only safe if the mm semaphore is held when called. */ +static int e1356_remap_page_range(unsigned long from, phys_t phys_addr, unsigned long size, pgprot_t prot) +{ + int error = 0; + pgd_t * dir; + phys_t beg = from; + phys_t end = from + size; + struct mm_struct *mm = current->mm; + + phys_addr -= from; + dir = pgd_offset(mm, from); + flush_cache_range(mm, beg, end); + if (from >= end) + BUG(); + + spin_lock(&mm->page_table_lock); + do { + pmd_t *pmd = pmd_alloc(mm, dir, from); + error = -ENOMEM; + if (!pmd) + break; + error = e1356_remap_pmd_range(mm, pmd, from, end - from, phys_addr + from, prot); + if (error) + break; + from = (from + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } while (from && (from < end)); + spin_unlock(&mm->page_table_lock); + flush_tlb_range(mm, beg, end); + return error; +} +#endif |