linux-mips
[Top] [All Lists]

Re: IP32 memory patch

To: linux-mips@linux-mips.org
Subject: Re: IP32 memory patch
From: "Ilya A. Volynets-Evenbakh" <ilya@total-knowledge.com>
Date: Sun, 09 Jan 2005 16:30:57 -0800
In-reply-to: <41E069BF.8040500@total-knowledge.com>
Organization: Total Knowledge
Original-recipient: rfc822;linux-mips@linux-mips.org
References: <41E069BF.8040500@total-knowledge.com>
Sender: linux-mips-bounce@linux-mips.org
User-agent: Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.7.5) Gecko/20041221
Ilya A. Volynets-Evenbakh wrote:

Hi all,

Here is patch to allow IP32 to access full 1G of memory.
I was able to boot an O2 with 576M of ram using NFS-root.

Two notes:
1. This patch will only work with latest linux-mips.org CVS tree
2. Seems that PCI support is messed up by this patch, so SCSI doesn't work.

If someone can see something obvious regarding #2, I'll be glad to hear it.

   Ilya.

So, I found the problem.
Memory above 256M is mapped at 0x40000000+256M in IP32. However, PCI sees whole 1G as contiguous chunk at offset 0. This means we have to translate dma addreses differently, depending on wether they fall within first
256M or not.

So, I created separate implemetation of pci_dma_* routines.

Index: arch/mips/Kconfig
===================================================================
RCS file: /home/cvs/linux/arch/mips/Kconfig,v
retrieving revision 1.126
diff -u -r1.126 Kconfig
--- arch/mips/Kconfig   27 Dec 2004 18:23:53 -0000      1.126
+++ arch/mips/Kconfig   10 Jan 2005 00:02:10 -0000
@@ -541,6 +541,8 @@
       select ARC
       select ARC32
       select BOOT_ELF32
+       select OWN_DMA
+       select DMA_IP32
       select DMA_NONCOHERENT
       select HW_HAS_PCI
       select R5000_CPU_SCACHE
@@ -926,6 +928,12 @@
config DMA_IP27
       bool

+config DMA_IP32
+       bool
+
+config OWN_DMA
+       bool
+
config DMA_NONCOHERENT
       bool
Index: arch/mips/mm/Makefile
===================================================================
RCS file: /home/cvs/linux/arch/mips/mm/Makefile,v
retrieving revision 1.74
diff -u -r1.74 Makefile
--- arch/mips/mm/Makefile       7 Jan 2005 18:58:34 -0000       1.74
+++ arch/mips/mm/Makefile       9 Jan 2005 23:53:54 -0000
@@ -34,8 +34,11 @@
#
# Choose one DMA coherency model
#
+ifndef CONFIG_OWN_DMA
obj-$(CONFIG_DMA_COHERENT)     += dma-coherent.o
obj-$(CONFIG_DMA_NONCOHERENT)  += dma-noncoherent.o
+endif
obj-$(CONFIG_DMA_IP27)         += dma-ip27.o
+obj-$(CONFIG_DMA_IP32)         += dma-ip32.o

EXTRA_AFLAGS := $(CFLAGS)

Index: arch/mips/sgi-ip32/Makefile
===================================================================
RCS file: /home/cvs/linux/arch/mips/sgi-ip32/Makefile,v
retrieving revision 1.9
diff -u -r1.9 Makefile
--- arch/mips/sgi-ip32/Makefile 26 Feb 2004 09:09:09 -0000      1.9
+++ arch/mips/sgi-ip32/Makefile 9 Jan 2005 23:53:55 -0000
@@ -4,6 +4,6 @@
#

obj-y += ip32-berr.o ip32-irq.o ip32-irq-glue.o ip32-setup.o ip32-reset.o \
-          crime.o
+          crime.o ip32-memory.o

EXTRA_AFLAGS := $(CFLAGS)
Index: arch/mips/sgi-ip32/crime.c
===================================================================
RCS file: /home/cvs/linux/arch/mips/sgi-ip32/crime.c,v
retrieving revision 1.6
diff -u -r1.6 crime.c
--- arch/mips/sgi-ip32/crime.c  31 Aug 2004 16:49:32 -0000      1.6
+++ arch/mips/sgi-ip32/crime.c  9 Jan 2005 23:53:55 -0000
@@ -24,7 +24,8 @@
{
       unsigned int id, rev;
       const int field = 2 * sizeof(unsigned long);
-
+
+ set_io_port_base((unsigned long) ioremap(MACEPCI_LOW_IO, 0x2000000));
       crime = ioremap(CRIME_BASE, sizeof(struct sgi_crime));
       mace = ioremap(MACE_BASE, sizeof(struct sgi_mace));

Index: include/asm-mips/ip32/crime.h
===================================================================
RCS file: /home/cvs/linux/include/asm-mips/ip32/crime.h,v
retrieving revision 1.6
diff -u -r1.6 crime.h
--- include/asm-mips/ip32/crime.h       4 Dec 2004 18:16:09 -0000       1.6
+++ include/asm-mips/ip32/crime.h       9 Jan 2005 23:54:26 -0000
@@ -156,4 +156,6 @@

extern struct sgi_crime *crime;

+#define CRIME_HI_MEM_BASE 0x40000000 /* this is where whole 1G of RAM is mapped */
+
#endif /* __ASM_CRIME_H__ */
Index: include/asm-mips/mach-ip32/spaces.h
===================================================================
RCS file: /home/cvs/linux/include/asm-mips/mach-ip32/spaces.h,v
retrieving revision 1.1
diff -u -r1.1 spaces.h
--- include/asm-mips/mach-ip32/spaces.h 15 Oct 2004 02:09:12 -0000      1.1
+++ include/asm-mips/mach-ip32/spaces.h 9 Jan 2005 23:54:26 -0000
@@ -12,10 +12,6 @@

#include <linux/config.h>

-/*
- * This handles the memory map.
- */
-#define PAGE_OFFSET            0xffffffff80000000

/*
 * Memory above this physical address will be considered highmem.
@@ -26,11 +22,7 @@
#define HIGHMEM_START          (1UL << 59UL)
#endif

-#ifdef CONFIG_DMA_NONCOHERENT
#define CAC_BASE               0x9800000000000000
-#else
-#define CAC_BASE               0xa800000000000000
-#endif
#define IO_BASE                        0x9000000000000000
#define UNCAC_BASE             0x9000000000000000
#define MAP_BASE               0xc000000000000000
@@ -39,4 +31,9 @@
#define TO_CAC(x)              (CAC_BASE   | ((x) & TO_PHYS_MASK))
#define TO_UNCAC(x)            (UNCAC_BASE | ((x) & TO_PHYS_MASK))

+/*
+ * This handles the memory map.
+ */
+#define PAGE_OFFSET            CAC_BASE
+
#endif /* __ASM_MACH_IP32_SPACES_H */
--- /dev/null   1969-12-31 16:00:00 -0800
+++ arch/mips/sgi-ip32/ip32-memory.c    2005-01-09 16:25:59 -0800
@@ -0,0 +1,53 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2003 Keith M Wesolowski
+ * Copyright (C) 2005 Ilya A. Volynets (Total Knowledge)
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include <asm/ip32/crime.h>
+#include <asm/bootinfo.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+
+extern void crime_init(void);
+
+void __init prom_meminit (void)
+{
+       u64 base, size;
+       int bank;
+
+       crime_init();
+
+       for (bank=0; bank < CRIME_MAXBANKS; bank++) {
+               u64 bankctl = crime->bank_ctrl[bank];
+               base = (bankctl & CRIME_MEM_BANK_CONTROL_ADDR) << 25;
+               if (bank != 0 && base == 0)
+                       continue;
+ size = (bankctl & CRIME_MEM_BANK_CONTROL_SDRAM_SIZE) ? 128 : 32;
+               size <<= 20;
+               if (base + size > (256 << 20)) {
+#if !defined(CONFIG_HIGHMEM) && PAGE_OFFSET!=CAC_BASE
+                   continue;
+#else
+                   base+=CRIME_HI_MEM_BASE;
+#endif
+               }
+               printk("CRIME MC: bank %u base 0x%016lx size %luMB\n",
+                       bank, base, size);
+               add_memory_region (base, size, BOOT_MEM_RAM);
+       }
+}
+
+
+unsigned long __init prom_free_prom_memory (void)
+{
+       return 0;
+}
--- /dev/null   1969-12-31 16:00:00 -0800
+++ arch/mips/mm/dma-ip32.c     2005-01-09 16:28:56 -0800
@@ -0,0 +1,367 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2000  Ani Joshi <ajoshi@unixbox.com>
+ * Copyright (C) 2000, 2001  Ralf Baechle <ralf@gnu.org>
+ * Copyright (C) 2005 Ilya A. Volynets-Evenbakh <ilya@total-knowledge.com>
+ * swiped from i386, and cloned for MIPS by Geert, polished by Ralf.
+ * IP32 changes by Ilya.
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/cache.h>
+#include <asm/io.h>
+#include <asm/ip32/crime.h>
+
+/*
+ * Warning on the terminology - Linux calls an uncached area coherent;
+ * MIPS terminology calls memory areas with hardware maintained coherency
+ * coherent.
+ */
+
+void *dma_alloc_noncoherent(struct device *dev, size_t size,
+       dma_addr_t * dma_handle, int gfp)
+{
+       void *ret;
+       /* ignore region specifiers */
+       gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);
+
+       if (dev == NULL || (dev->coherent_dma_mask < 0xffffffff))
+               gfp |= GFP_DMA;
+       ret = (void *) __get_free_pages(gfp, get_order(size));
+
+       if (ret != NULL) {
+               u64 addr = virt_to_phys(ret);
+               memset(ret, 0, size);
+               if(addr > CRIME_HI_MEM_BASE)
+                   addr-= CRIME_HI_MEM_BASE;
+               *dma_handle = addr;
+       }
+
+       return ret;
+}
+
+EXPORT_SYMBOL(dma_alloc_noncoherent);
+
+void *dma_alloc_coherent(struct device *dev, size_t size,
+       dma_addr_t * dma_handle, int gfp)
+{
+       void *ret;
+
+       ret = dma_alloc_noncoherent(dev, size, dma_handle, gfp);
+       if (ret) {
+               dma_cache_wback_inv((unsigned long) ret, size);
+               ret = UNCAC_ADDR(ret);
+       }
+
+       return ret;
+}
+
+EXPORT_SYMBOL(dma_alloc_coherent);
+
+void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr,
+       dma_addr_t dma_handle)
+{
+       free_pages((unsigned long) vaddr, get_order(size));
+}
+
+EXPORT_SYMBOL(dma_free_noncoherent);
+
+void dma_free_coherent(struct device *dev, size_t size, void *vaddr,
+       dma_addr_t dma_handle)
+{
+       unsigned long addr = (unsigned long) vaddr;
+
+       addr = CAC_ADDR(addr);
+       free_pages(addr, get_order(size));
+}
+
+EXPORT_SYMBOL(dma_free_coherent);
+
+static inline void __dma_sync(unsigned long addr, size_t size,
+       enum dma_data_direction direction)
+{
+       switch (direction) {
+       case DMA_TO_DEVICE:
+               dma_cache_wback(addr, size);
+               break;
+
+       case DMA_FROM_DEVICE:
+               dma_cache_inv(addr, size);
+               break;
+
+       case DMA_BIDIRECTIONAL:
+               dma_cache_wback_inv(addr, size);
+               break;
+
+       default:
+               BUG();
+       }
+}
+
+dma_addr_t dma_map_single(struct device *dev, void *ptr, size_t size,
+       enum dma_data_direction direction)
+{
+       unsigned long addr = (unsigned long) ptr;
+
+       switch (direction) {
+       case DMA_TO_DEVICE:
+               dma_cache_wback(addr, size);
+               break;
+
+       case DMA_FROM_DEVICE:
+               dma_cache_inv(addr, size);
+               break;
+
+       case DMA_BIDIRECTIONAL:
+               dma_cache_wback_inv(addr, size);
+               break;
+
+       default:
+               BUG();
+       }
+
+       addr = virt_to_phys(ptr);
+       if(addr>=CRIME_HI_MEM_BASE)
+           addr-=CRIME_HI_MEM_BASE;
+       return (dma_addr_t)addr;
+}
+
+EXPORT_SYMBOL(dma_map_single);
+
+void dma_unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
+       enum dma_data_direction direction)
+{
+       switch (direction) {
+       case DMA_TO_DEVICE:
+               break;
+
+       case DMA_FROM_DEVICE:
+               break;
+
+       case DMA_BIDIRECTIONAL:
+               break;
+
+       default:
+               BUG();
+       }
+}
+
+EXPORT_SYMBOL(dma_unmap_single);
+
+int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents,
+       enum dma_data_direction direction)
+{
+       int i;
+
+       BUG_ON(direction == DMA_NONE);
+
+       for (i = 0; i < nents; i++, sg++) {
+               unsigned long addr;
+
+               addr = (unsigned long) page_address(sg->page)+sg->offset;
+               if (addr)
+                       __dma_sync(addr, sg->length, direction);
+               addr = __pa(addr);
+               if(addr >= CRIME_HI_MEM_BASE)
+                       addr -=  CRIME_HI_MEM_BASE;
+               sg->dma_address = (dma_addr_t)addr;
+       }
+
+       return nents;
+}
+
+EXPORT_SYMBOL(dma_map_sg);
+
+dma_addr_t dma_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size, enum dma_data_direction direction)
+{
+       unsigned long addr;
+
+       BUG_ON(direction == DMA_NONE);
+
+       addr = (unsigned long) page_address(page) + offset;
+       dma_cache_wback_inv(addr, size);
+       addr = __pa(addr);
+       if(addr >= CRIME_HI_MEM_BASE)
+               addr -=  CRIME_HI_MEM_BASE;
+
+       return (dma_addr_t)addr;
+}
+
+EXPORT_SYMBOL(dma_map_page);
+
+void dma_unmap_page(struct device *dev, dma_addr_t dma_address, size_t size,
+       enum dma_data_direction direction)
+{
+       BUG_ON(direction == DMA_NONE);
+
+       if (direction != DMA_TO_DEVICE) {
+               unsigned long addr;
+
+               addr = dma_address + PAGE_OFFSET;
+               if(dma_address>=256*1024*1024)
+                   addr+=CRIME_HI_MEM_BASE;
+               dma_cache_wback_inv(addr, size);
+       }
+}
+
+EXPORT_SYMBOL(dma_unmap_page);
+
+void dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nhwentries,
+       enum dma_data_direction direction)
+{
+       unsigned long addr;
+       int i;
+
+       BUG_ON(direction == DMA_NONE);
+
+       if (direction == DMA_TO_DEVICE)
+               return;
+
+       for (i = 0; i < nhwentries; i++, sg++) {
+               addr = (unsigned long) page_address(sg->page);
+               if (!addr)
+                       continue;
+               dma_cache_wback_inv(addr + sg->offset, sg->length);
+       }
+}
+
+EXPORT_SYMBOL(dma_unmap_sg);
+
+void dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
+       size_t size, enum dma_data_direction direction)
+{
+       unsigned long addr;
+
+       BUG_ON(direction == DMA_NONE);
+
+       addr = dma_handle + PAGE_OFFSET;
+       if(dma_handle>=256*1024*1024)
+           addr+=CRIME_HI_MEM_BASE;
+       __dma_sync(addr, size, direction);
+}
+
+EXPORT_SYMBOL(dma_sync_single_for_cpu);
+
+void dma_sync_single_for_device(struct device *dev, dma_addr_t dma_handle,
+       size_t size, enum dma_data_direction direction)
+{
+       unsigned long addr;
+
+       BUG_ON(direction == DMA_NONE);
+
+       addr = dma_handle + PAGE_OFFSET;
+       if(dma_handle>=256*1024*1024)
+           addr+=CRIME_HI_MEM_BASE;
+       __dma_sync(addr, size, direction);
+}
+
+EXPORT_SYMBOL(dma_sync_single_for_device);
+
+void dma_sync_single_range_for_cpu(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, enum dma_data_direction direction)
+{
+       unsigned long addr;
+
+       BUG_ON(direction == DMA_NONE);
+
+       addr = dma_handle + offset + PAGE_OFFSET;
+       if(dma_handle>=256*1024*1024)
+           addr+=CRIME_HI_MEM_BASE;
+       __dma_sync(addr, size, direction);
+}
+
+EXPORT_SYMBOL(dma_sync_single_range_for_cpu);
+
+void dma_sync_single_range_for_device(struct device *dev, dma_addr_t dma_handle, + unsigned long offset, size_t size, enum dma_data_direction direction)
+{
+       unsigned long addr;
+
+       BUG_ON(direction == DMA_NONE);
+
+       addr = dma_handle + offset + PAGE_OFFSET;
+       if(dma_handle>=256*1024*1024)
+           addr+=CRIME_HI_MEM_BASE;
+       __dma_sync(addr, size, direction);
+}
+
+EXPORT_SYMBOL(dma_sync_single_range_for_device);
+
+void dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg, int nelems,
+       enum dma_data_direction direction)
+{
+       int i;
+
+       BUG_ON(direction == DMA_NONE);
+
+       /* Make sure that gcc doesn't leave the empty loop body.  */
+       for (i = 0; i < nelems; i++, sg++)
+               __dma_sync((unsigned long)page_address(sg->page),
+                          sg->length, direction);
+}
+
+EXPORT_SYMBOL(dma_sync_sg_for_cpu);
+
+void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nelems,
+       enum dma_data_direction direction)
+{
+       int i;
+
+       BUG_ON(direction == DMA_NONE);
+
+       /* Make sure that gcc doesn't leave the empty loop body.  */
+       for (i = 0; i < nelems; i++, sg++)
+               __dma_sync((unsigned long)page_address(sg->page),
+                          sg->length, direction);
+}
+
+EXPORT_SYMBOL(dma_sync_sg_for_device);
+
+int dma_mapping_error(dma_addr_t dma_addr)
+{
+       return 0;
+}
+
+EXPORT_SYMBOL(dma_mapping_error);
+
+int dma_supported(struct device *dev, u64 mask)
+{
+       /*
+        * we fall back to GFP_DMA when the mask isn't all 1s,
+        * so we can't guarantee allocations that must be
+        * within a tighter range than GFP_DMA..
+        */
+       if (mask < 0x00ffffff)
+               return 0;
+
+       return 1;
+}
+
+EXPORT_SYMBOL(dma_supported);
+
+int dma_is_consistent(dma_addr_t dma_addr)
+{
+       return 1;
+}
+
+EXPORT_SYMBOL(dma_is_consistent);
+
+void dma_cache_sync(void *vaddr, size_t size, enum dma_data_direction direction)
+{
+       if (direction == DMA_NONE)
+               return;
+
+       dma_cache_wback_inv((unsigned long)vaddr, size);
+}
+
+EXPORT_SYMBOL(dma_cache_sync);
+
--- /dev/null   1969-12-31 16:00:00 -0800
+++ include/asm-mips/mach-ip32/cpu-feature-overrides.h 2005-01-08 21:59:51 -0800
@@ -0,0 +1,22 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004 Ilya A. Volynets-Evenbakh (Total Knowledge)
+ */
+#ifndef __ASM_MACH_IP32_CPU_FEATURE_OVERRIDES_H
+#define __ASM_MACH_IP32_CPU_FEATURE_OVERRIDES_H
+
+/*
+ * R5000 has an interesting "restriction":  ll(d)/sc(d)
+ * instructions to XKPHYS region simply do uncached bus
+ * requests. This breaks all the atomic bitops functions.
+ * so, for 64bit IP32 kernel we just don't use ll/sc.
+ * This does not affect luserland.
+ */
+#if defined(CONFIG_CPU_R5000) && defined(CONFIG_MIPS64)
+#define cpu_has_llsc   0
+#endif
+
+#endif /* __ASM_MACH_IP32_CPU_FEATURE_OVERRIDES_H */
Index: arch/mips/sgi-ip32/ip32-setup.c
===================================================================
RCS file: /home/cvs/linux/arch/mips/sgi-ip32/ip32-setup.c,v
retrieving revision 1.22
diff -u -r1.22 ip32-setup.c
--- arch/mips/sgi-ip32/ip32-setup.c     31 Aug 2004 16:49:32 -0000      1.22
+++ arch/mips/sgi-ip32/ip32-setup.c     10 Jan 2005 00:18:02 -0000
@@ -94,10 +94,6 @@

static int __init ip32_setup(void)
{
- set_io_port_base((unsigned long) ioremap(MACEPCI_LOW_IO, 0x2000000));
-
-       crime_init();
-
       board_be_init = ip32_be_init;

       rtc_get_time = mc146818_get_cmos_time;


<Prev in Thread] Current Thread [Next in Thread>