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
|