mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 10:49:33 +00:00
MIPS: PowerTV: Use O(1) algorthm for phys_to_dma/dma_to_phys
Replace phys_to_dma()/dma_to_phys() looping algorithm with an O(1) algorithm The approach taken is inspired by the sparse memory implementation: take a certain number of high-order bits off the address them, use this as an index into a table containing an offset to the desired address and add it to the original value. There is a table for mapping physical addresses to DMA addresses and another one for the reverse mapping. The table sizes depend on how fine-grained the mappings need to be; Coarser granularity less to smaller tables. On a processor with 32-bit physical and DMA addresses, with 4 MIB granularity, memory usage is two 2048-byte arrays. Each 32-byte cache line thus covers 64 MiB of address space. Also, renames phys_to_bus() to phys_to_dma() and bus_to_phys() to dma_to_phys() to align with kernel usage. [Ralf: Fixed silly build breakage due to stackoverflow warning caused by huge array on stack.] Signed-off-by: David VomLehn <dvomlehn@cisco.com> To: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/1257/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
parent
36f217d9df
commit
ca36c36b78
6 changed files with 533 additions and 146 deletions
|
@ -65,21 +65,21 @@ static inline dma_addr_t plat_map_dma_mem(struct device *dev, void *addr,
|
|||
size_t size)
|
||||
{
|
||||
if (is_kseg2(addr))
|
||||
return phys_to_bus(virt_to_phys_from_pte(addr));
|
||||
return phys_to_dma(virt_to_phys_from_pte(addr));
|
||||
else
|
||||
return phys_to_bus(virt_to_phys(addr));
|
||||
return phys_to_dma(virt_to_phys(addr));
|
||||
}
|
||||
|
||||
static inline dma_addr_t plat_map_dma_mem_page(struct device *dev,
|
||||
struct page *page)
|
||||
{
|
||||
return phys_to_bus(page_to_phys(page));
|
||||
return phys_to_dma(page_to_phys(page));
|
||||
}
|
||||
|
||||
static inline unsigned long plat_dma_addr_to_phys(struct device *dev,
|
||||
dma_addr_t dma_addr)
|
||||
{
|
||||
return bus_to_phys(dma_addr);
|
||||
return dma_to_phys(dma_addr);
|
||||
}
|
||||
|
||||
static inline void plat_unmap_dma_mem(struct device *dev, dma_addr_t dma_addr,
|
||||
|
|
|
@ -10,64 +10,101 @@
|
|||
#define __ASM_MACH_POWERTV_IOREMAP_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
#define LOW_MEM_BOUNDARY_PHYS 0x20000000
|
||||
#define LOW_MEM_BOUNDARY_MASK (~(LOW_MEM_BOUNDARY_PHYS - 1))
|
||||
#include <asm/pgtable-bits.h>
|
||||
#include <asm/addrspace.h>
|
||||
|
||||
/* We're going to mess with bits, so get sizes */
|
||||
#define IOR_BPC 8 /* Bits per char */
|
||||
#define IOR_PHYS_BITS (IOR_BPC * sizeof(phys_addr_t))
|
||||
#define IOR_DMA_BITS (IOR_BPC * sizeof(dma_addr_t))
|
||||
|
||||
/*
|
||||
* The bus addresses are different than the physical addresses that
|
||||
* the processor sees by an offset. This offset varies by ASIC
|
||||
* version. Define a variable to hold the offset and some macros to
|
||||
* make the conversion simpler. */
|
||||
extern unsigned long phys_to_bus_offset;
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
#define MEM_GAP_PHYS 0x60000000
|
||||
/*
|
||||
* TODO: We will use the hard code for conversion between physical and
|
||||
* bus until the bootloader releases their device tree to us.
|
||||
* Define the granularity of physical/DMA mapping in terms of the number
|
||||
* of bits that defines the offset within a grain. These will be the
|
||||
* least significant bits of the address. The rest of a physical or DMA
|
||||
* address will be used to index into an appropriate table to find the
|
||||
* offset to add to the address to yield the corresponding DMA or physical
|
||||
* address, respectively.
|
||||
*/
|
||||
#define phys_to_bus(x) (((x) < LOW_MEM_BOUNDARY_PHYS) ? \
|
||||
((x) + phys_to_bus_offset) : (x))
|
||||
#define bus_to_phys(x) (((x) < MEM_GAP_PHYS_ADDR) ? \
|
||||
((x) - phys_to_bus_offset) : (x))
|
||||
#else
|
||||
#define phys_to_bus(x) ((x) + phys_to_bus_offset)
|
||||
#define bus_to_phys(x) ((x) - phys_to_bus_offset)
|
||||
#endif
|
||||
#define IOR_LSBITS 22 /* Bits in a grain */
|
||||
|
||||
/*
|
||||
* Determine whether the address we are given is for an ASIC device
|
||||
* Params: addr Address to check
|
||||
* Returns: Zero if the address is not for ASIC devices, non-zero
|
||||
* if it is.
|
||||
* Compute the number of most significant address bits after removing those
|
||||
* used for the offset within a grain and then compute the number of table
|
||||
* entries for the conversion.
|
||||
*/
|
||||
static inline int asic_is_device_addr(phys_t addr)
|
||||
#define IOR_PHYS_MSBITS (IOR_PHYS_BITS - IOR_LSBITS)
|
||||
#define IOR_NUM_PHYS_TO_DMA ((phys_addr_t) 1 << IOR_PHYS_MSBITS)
|
||||
|
||||
#define IOR_DMA_MSBITS (IOR_DMA_BITS - IOR_LSBITS)
|
||||
#define IOR_NUM_DMA_TO_PHYS ((dma_addr_t) 1 << IOR_DMA_MSBITS)
|
||||
|
||||
/*
|
||||
* Define data structures used as elements in the arrays for the conversion
|
||||
* between physical and DMA addresses. We do some slightly fancy math to
|
||||
* compute the width of the offset element of the conversion tables so
|
||||
* that we can have the smallest conversion tables. Next, round up the
|
||||
* sizes to the next higher power of two, i.e. the offset element will have
|
||||
* 8, 16, 32, 64, etc. bits. This eliminates the need to mask off any
|
||||
* bits. Finally, we compute a shift value that puts the most significant
|
||||
* bits of the offset into the most significant bits of the offset element.
|
||||
* This makes it more efficient on processors without barrel shifters and
|
||||
* easier to see the values if the conversion table is dumped in binary.
|
||||
*/
|
||||
#define _IOR_OFFSET_WIDTH(n) (1 << order_base_2(n))
|
||||
#define IOR_OFFSET_WIDTH(n) \
|
||||
(_IOR_OFFSET_WIDTH(n) < 8 ? 8 : _IOR_OFFSET_WIDTH(n))
|
||||
|
||||
#define IOR_PHYS_OFFSET_BITS IOR_OFFSET_WIDTH(IOR_PHYS_MSBITS)
|
||||
#define IOR_PHYS_SHIFT (IOR_PHYS_BITS - IOR_PHYS_OFFSET_BITS)
|
||||
|
||||
#define IOR_DMA_OFFSET_BITS IOR_OFFSET_WIDTH(IOR_DMA_MSBITS)
|
||||
#define IOR_DMA_SHIFT (IOR_DMA_BITS - IOR_DMA_OFFSET_BITS)
|
||||
|
||||
struct ior_phys_to_dma {
|
||||
dma_addr_t offset:IOR_DMA_OFFSET_BITS __packed
|
||||
__aligned((IOR_DMA_OFFSET_BITS / IOR_BPC));
|
||||
};
|
||||
|
||||
struct ior_dma_to_phys {
|
||||
dma_addr_t offset:IOR_PHYS_OFFSET_BITS __packed
|
||||
__aligned((IOR_PHYS_OFFSET_BITS / IOR_BPC));
|
||||
};
|
||||
|
||||
extern struct ior_phys_to_dma _ior_phys_to_dma[IOR_NUM_PHYS_TO_DMA];
|
||||
extern struct ior_dma_to_phys _ior_dma_to_phys[IOR_NUM_DMA_TO_PHYS];
|
||||
|
||||
static inline dma_addr_t _phys_to_dma_offset_raw(phys_addr_t phys)
|
||||
{
|
||||
return !((phys_t)addr & (phys_t) LOW_MEM_BOUNDARY_MASK);
|
||||
return (dma_addr_t)_ior_phys_to_dma[phys >> IOR_LSBITS].offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine whether the address we are given is external RAM mappable
|
||||
* into KSEG1.
|
||||
* Params: addr Address to check
|
||||
* Returns: Zero if the address is not for external RAM and
|
||||
*/
|
||||
static inline int asic_is_lowmem_ram_addr(phys_t addr)
|
||||
static inline dma_addr_t _dma_to_phys_offset_raw(dma_addr_t dma)
|
||||
{
|
||||
/*
|
||||
* The RAM always starts at the following address in the processor's
|
||||
* physical address space
|
||||
*/
|
||||
static const phys_t phys_ram_base = 0x10000000;
|
||||
phys_t bus_ram_base;
|
||||
|
||||
bus_ram_base = phys_to_bus_offset + phys_ram_base;
|
||||
|
||||
return addr >= bus_ram_base &&
|
||||
addr < (bus_ram_base + (LOW_MEM_BOUNDARY_PHYS - phys_ram_base));
|
||||
return (dma_addr_t)_ior_dma_to_phys[dma >> IOR_LSBITS].offset;
|
||||
}
|
||||
|
||||
/* These are not portable and should not be used in drivers. Drivers should
|
||||
* be using ioremap() and friends to map physical addreses to virtual
|
||||
* addresses and dma_map*() and friends to map virtual addresses into DMA
|
||||
* addresses and back.
|
||||
*/
|
||||
static inline dma_addr_t phys_to_dma(phys_addr_t phys)
|
||||
{
|
||||
return phys + (_phys_to_dma_offset_raw(phys) << IOR_PHYS_SHIFT);
|
||||
}
|
||||
|
||||
static inline phys_addr_t dma_to_phys(dma_addr_t dma)
|
||||
{
|
||||
return dma + (_dma_to_phys_offset_raw(dma) << IOR_DMA_SHIFT);
|
||||
}
|
||||
|
||||
extern void ioremap_add_map(dma_addr_t phys, phys_addr_t alias,
|
||||
dma_addr_t size);
|
||||
|
||||
/*
|
||||
* Allow physical addresses to be fixed up to help peripherals located
|
||||
* outside the low 32-bit range -- generic pass-through version.
|
||||
|
@ -77,10 +114,50 @@ static inline phys_t fixup_bigphys_addr(phys_t phys_addr, phys_t size)
|
|||
return phys_addr;
|
||||
}
|
||||
|
||||
static inline void __iomem *plat_ioremap(phys_t offset, unsigned long size,
|
||||
/*
|
||||
* Handle the special case of addresses the area aliased into the first
|
||||
* 512 MiB of the processor's physical address space. These turn into either
|
||||
* kseg0 or kseg1 addresses, depending on flags.
|
||||
*/
|
||||
static inline void __iomem *plat_ioremap(phys_t start, unsigned long size,
|
||||
unsigned long flags)
|
||||
{
|
||||
return NULL;
|
||||
phys_addr_t start_offset;
|
||||
void __iomem *result = NULL;
|
||||
|
||||
/* Start by checking to see whether this is an aliased address */
|
||||
start_offset = _dma_to_phys_offset_raw(start);
|
||||
|
||||
/*
|
||||
* If:
|
||||
* o the memory is aliased into the first 512 MiB, and
|
||||
* o the start and end are in the same RAM bank, and
|
||||
* o we don't have a zero size or wrap around, and
|
||||
* o we are supposed to create an uncached mapping,
|
||||
* handle this is a kseg0 or kseg1 address
|
||||
*/
|
||||
if (start_offset != 0) {
|
||||
phys_addr_t last;
|
||||
dma_addr_t dma_to_phys_offset;
|
||||
|
||||
last = start + size - 1;
|
||||
dma_to_phys_offset =
|
||||
_dma_to_phys_offset_raw(last) << IOR_DMA_SHIFT;
|
||||
|
||||
if (dma_to_phys_offset == start_offset &&
|
||||
size != 0 && start <= last) {
|
||||
phys_t adjusted_start;
|
||||
adjusted_start = start + start_offset;
|
||||
if (flags == _CACHE_UNCACHED)
|
||||
result = (void __iomem *) (unsigned long)
|
||||
CKSEG1ADDR(adjusted_start);
|
||||
else
|
||||
result = (void __iomem *) (unsigned long)
|
||||
CKSEG0ADDR(adjusted_start);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline int plat_iounmap(const volatile void __iomem *addr)
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
# under Linux.
|
||||
#
|
||||
|
||||
obj-y += init.o memory.o reset.o time.o powertv_setup.o asic/ pci/
|
||||
obj-y += init.o ioremap.o memory.o powertv_setup.o reset.o time.o \
|
||||
asic/ pci/
|
||||
|
||||
EXTRA_CFLAGS += -Wall -Werror
|
||||
|
|
|
@ -80,8 +80,8 @@ static bool usb_configured;
|
|||
* Don't recommend to use it directly, it is usually used by kernel internally.
|
||||
* Portable code should be using interfaces such as ioremp, dma_map_single, etc.
|
||||
*/
|
||||
unsigned long phys_to_bus_offset;
|
||||
EXPORT_SYMBOL(phys_to_bus_offset);
|
||||
unsigned long phys_to_dma_offset;
|
||||
EXPORT_SYMBOL(phys_to_dma_offset);
|
||||
|
||||
/*
|
||||
*
|
||||
|
@ -533,10 +533,10 @@ void __init configure_platform(void)
|
|||
|
||||
switch (asic) {
|
||||
case ASIC_ZEUS:
|
||||
phys_to_bus_offset = 0x30000000;
|
||||
phys_to_dma_offset = 0x30000000;
|
||||
break;
|
||||
case ASIC_CALLIOPE:
|
||||
phys_to_bus_offset = 0x10000000;
|
||||
phys_to_dma_offset = 0x10000000;
|
||||
break;
|
||||
case ASIC_CRONUSLITE:
|
||||
/* Fall through */
|
||||
|
@ -546,10 +546,10 @@ void __init configure_platform(void)
|
|||
* 0x2XXXXXXX. If 0x10000000 aliases into 0x60000000-
|
||||
* 0x6XXXXXXX, the offset should be 0x50000000, not 0x10000000.
|
||||
*/
|
||||
phys_to_bus_offset = 0x10000000;
|
||||
phys_to_dma_offset = 0x10000000;
|
||||
break;
|
||||
default:
|
||||
phys_to_bus_offset = 0x00000000;
|
||||
phys_to_dma_offset = 0x00000000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -603,7 +603,7 @@ void __init platform_alloc_bootmem(void)
|
|||
int size = gp_resources[i].end - gp_resources[i].start + 1;
|
||||
if ((gp_resources[i].start != 0) &&
|
||||
((gp_resources[i].flags & IORESOURCE_MEM) != 0)) {
|
||||
reserve_bootmem(bus_to_phys(gp_resources[i].start),
|
||||
reserve_bootmem(dma_to_phys(gp_resources[i].start),
|
||||
size, 0);
|
||||
total += gp_resources[i].end -
|
||||
gp_resources[i].start + 1;
|
||||
|
@ -627,7 +627,7 @@ void __init platform_alloc_bootmem(void)
|
|||
|
||||
else {
|
||||
gp_resources[i].start =
|
||||
phys_to_bus(virt_to_phys(mem));
|
||||
phys_to_dma(virt_to_phys(mem));
|
||||
gp_resources[i].end =
|
||||
gp_resources[i].start + size - 1;
|
||||
total += size;
|
||||
|
@ -691,7 +691,7 @@ static void __init pmem_setup_resource(void)
|
|||
if (resource && pmemaddr && pmemlen) {
|
||||
/* The address provided by bootloader is in kseg0. Convert to
|
||||
* a bus address. */
|
||||
resource->start = phys_to_bus(pmemaddr - 0x80000000);
|
||||
resource->start = phys_to_dma(pmemaddr - 0x80000000);
|
||||
resource->end = resource->start + pmemlen - 1;
|
||||
|
||||
pr_info("persistent memory: start=0x%x end=0x%x\n",
|
||||
|
|
136
arch/mips/powertv/ioremap.c
Normal file
136
arch/mips/powertv/ioremap.c
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* ioremap.c
|
||||
*
|
||||
* Support for mapping between dma_addr_t values a phys_addr_t values.
|
||||
*
|
||||
* Copyright (C) 2005-2009 Scientific-Atlanta, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Author: David VomLehn <dvomlehn@cisco.com>
|
||||
*
|
||||
* Description: Defines the platform resources for the SA settop.
|
||||
*
|
||||
* NOTE: The bootloader allocates persistent memory at an address which is
|
||||
* 16 MiB below the end of the highest address in KSEG0. All fixed
|
||||
* address memory reservations must avoid this region.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/mach-powertv/ioremap.h>
|
||||
|
||||
/*
|
||||
* Define the sizes of and masks for grains in physical and DMA space. The
|
||||
* values are the same but the types are not.
|
||||
*/
|
||||
#define IOR_PHYS_GRAIN ((phys_addr_t) 1 << IOR_LSBITS)
|
||||
#define IOR_PHYS_GRAIN_MASK (IOR_PHYS_GRAIN - 1)
|
||||
|
||||
#define IOR_DMA_GRAIN ((dma_addr_t) 1 << IOR_LSBITS)
|
||||
#define IOR_DMA_GRAIN_MASK (IOR_DMA_GRAIN - 1)
|
||||
|
||||
/*
|
||||
* Values that, when accessed by an index derived from a phys_addr_t and
|
||||
* added to phys_addr_t value, yield a DMA address
|
||||
*/
|
||||
struct ior_phys_to_dma _ior_phys_to_dma[IOR_NUM_PHYS_TO_DMA];
|
||||
EXPORT_SYMBOL(_ior_phys_to_dma);
|
||||
|
||||
/*
|
||||
* Values that, when accessed by an index derived from a dma_addr_t and
|
||||
* added to that dma_addr_t value, yield a physical address
|
||||
*/
|
||||
struct ior_dma_to_phys _ior_dma_to_phys[IOR_NUM_DMA_TO_PHYS];
|
||||
EXPORT_SYMBOL(_ior_dma_to_phys);
|
||||
|
||||
/**
|
||||
* setup_dma_to_phys - set up conversion from DMA to physical addresses
|
||||
* @dma_idx: Top IOR_LSBITS bits of the DMA address, i.e. an index
|
||||
* into the array _dma_to_phys.
|
||||
* @delta: Value that, when added to the DMA address, will yield the
|
||||
* physical address
|
||||
* @s: Number of bytes in the section of memory with the given delta
|
||||
* between DMA and physical addresses.
|
||||
*/
|
||||
static void setup_dma_to_phys(dma_addr_t dma, phys_addr_t delta, dma_addr_t s)
|
||||
{
|
||||
int dma_idx, first_idx, last_idx;
|
||||
phys_addr_t first, last;
|
||||
|
||||
/*
|
||||
* Calculate the first and last indices, rounding the first up and
|
||||
* the second down.
|
||||
*/
|
||||
first = dma & ~IOR_DMA_GRAIN_MASK;
|
||||
last = (dma + s - 1) & ~IOR_DMA_GRAIN_MASK;
|
||||
first_idx = first >> IOR_LSBITS; /* Convert to indices */
|
||||
last_idx = last >> IOR_LSBITS;
|
||||
|
||||
for (dma_idx = first_idx; dma_idx <= last_idx; dma_idx++)
|
||||
_ior_dma_to_phys[dma_idx].offset = delta >> IOR_DMA_SHIFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* setup_phys_to_dma - set up conversion from DMA to physical addresses
|
||||
* @phys_idx: Top IOR_LSBITS bits of the DMA address, i.e. an index
|
||||
* into the array _phys_to_dma.
|
||||
* @delta: Value that, when added to the DMA address, will yield the
|
||||
* physical address
|
||||
* @s: Number of bytes in the section of memory with the given delta
|
||||
* between DMA and physical addresses.
|
||||
*/
|
||||
static void setup_phys_to_dma(phys_addr_t phys, dma_addr_t delta, phys_addr_t s)
|
||||
{
|
||||
int phys_idx, first_idx, last_idx;
|
||||
phys_addr_t first, last;
|
||||
|
||||
/*
|
||||
* Calculate the first and last indices, rounding the first up and
|
||||
* the second down.
|
||||
*/
|
||||
first = phys & ~IOR_PHYS_GRAIN_MASK;
|
||||
last = (phys + s - 1) & ~IOR_PHYS_GRAIN_MASK;
|
||||
first_idx = first >> IOR_LSBITS; /* Convert to indices */
|
||||
last_idx = last >> IOR_LSBITS;
|
||||
|
||||
for (phys_idx = first_idx; phys_idx <= last_idx; phys_idx++)
|
||||
_ior_phys_to_dma[phys_idx].offset = delta >> IOR_PHYS_SHIFT;
|
||||
}
|
||||
|
||||
/**
|
||||
* ioremap_add_map - add to the physical and DMA address conversion arrays
|
||||
* @phys: Process's view of the address of the start of the memory chunk
|
||||
* @dma: DMA address of the start of the memory chunk
|
||||
* @size: Size, in bytes, of the chunk of memory
|
||||
*
|
||||
* NOTE: It might be obvious, but the assumption is that all @size bytes have
|
||||
* the same offset between the physical address and the DMA address.
|
||||
*/
|
||||
void ioremap_add_map(phys_addr_t phys, phys_addr_t dma, phys_addr_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
if ((dma & IOR_DMA_GRAIN_MASK) != 0 ||
|
||||
(phys & IOR_PHYS_GRAIN_MASK) != 0 ||
|
||||
(size & IOR_PHYS_GRAIN_MASK) != 0)
|
||||
pr_crit("Memory allocation must be in chunks of 0x%x bytes\n",
|
||||
IOR_PHYS_GRAIN);
|
||||
|
||||
setup_dma_to_phys(dma, phys - dma, size);
|
||||
setup_phys_to_dma(phys, dma - phys, size);
|
||||
}
|
|
@ -30,28 +30,141 @@
|
|||
#include <asm/sections.h>
|
||||
|
||||
#include <asm/mips-boards/prom.h>
|
||||
#include <asm/mach-powertv/asic.h>
|
||||
#include <asm/mach-powertv/ioremap.h>
|
||||
|
||||
#include "init.h"
|
||||
|
||||
/* Memory constants */
|
||||
#define KIBIBYTE(n) ((n) * 1024) /* Number of kibibytes */
|
||||
#define MEBIBYTE(n) ((n) * KIBIBYTE(1024)) /* Number of mebibytes */
|
||||
#define DEFAULT_MEMSIZE MEBIBYTE(256) /* If no memsize provided */
|
||||
#define LOW_MEM_MAX MEBIBYTE(252) /* Max usable low mem */
|
||||
#define RES_BOOTLDR_MEMSIZE MEBIBYTE(1) /* Memory reserved for bldr */
|
||||
#define BOOT_MEM_SIZE KIBIBYTE(256) /* Memory reserved for bldr */
|
||||
#define PHYS_MEM_START 0x10000000 /* Start of physical memory */
|
||||
#define DEFAULT_MEMSIZE MEBIBYTE(128) /* If no memsize provided */
|
||||
|
||||
char __initdata cmdline[COMMAND_LINE_SIZE];
|
||||
#define BLDR_SIZE KIBIBYTE(256) /* Memory reserved for bldr */
|
||||
#define RV_SIZE MEBIBYTE(4) /* Size of reset vector */
|
||||
|
||||
void __init prom_meminit(void)
|
||||
#define LOW_MEM_END 0x20000000 /* Highest low memory address */
|
||||
#define BLDR_ALIAS 0x10000000 /* Bootloader address */
|
||||
#define RV_PHYS 0x1fc00000 /* Reset vector address */
|
||||
#define LOW_RAM_END RV_PHYS /* End of real RAM in low mem */
|
||||
|
||||
/*
|
||||
* Very low-level conversion from processor physical address to device
|
||||
* DMA address for the first bank of memory.
|
||||
*/
|
||||
#define PHYS_TO_DMA(paddr) ((paddr) + (CONFIG_LOW_RAM_DMA - LOW_RAM_ALIAS))
|
||||
|
||||
unsigned long ptv_memsize;
|
||||
|
||||
/*
|
||||
* struct low_mem_reserved - Items in low memmory that are reserved
|
||||
* @start: Physical address of item
|
||||
* @size: Size, in bytes, of this item
|
||||
* @is_aliased: True if this is RAM aliased from another location. If false,
|
||||
* it is something other than aliased RAM and the RAM in the
|
||||
* unaliased address is still visible outside of low memory.
|
||||
*/
|
||||
struct low_mem_reserved {
|
||||
phys_addr_t start;
|
||||
phys_addr_t size;
|
||||
bool is_aliased;
|
||||
};
|
||||
|
||||
/*
|
||||
* Must be in ascending address order
|
||||
*/
|
||||
struct low_mem_reserved low_mem_reserved[] = {
|
||||
{BLDR_ALIAS, BLDR_SIZE, true}, /* Bootloader RAM */
|
||||
{RV_PHYS, RV_SIZE, false}, /* Reset vector */
|
||||
};
|
||||
|
||||
/*
|
||||
* struct mem_layout - layout of a piece of the system RAM
|
||||
* @phys: Physical address of the start of this piece of RAM. This is the
|
||||
* address at which both the processor and I/O devices see the
|
||||
* RAM.
|
||||
* @alias: Alias of this piece of memory in order to make it appear in
|
||||
* the low memory part of the processor's address space. I/O
|
||||
* devices don't see anything here.
|
||||
* @size: Size, in bytes, of this piece of RAM
|
||||
*/
|
||||
struct mem_layout {
|
||||
phys_addr_t phys;
|
||||
phys_addr_t alias;
|
||||
phys_addr_t size;
|
||||
};
|
||||
|
||||
/*
|
||||
* struct mem_layout_list - list descriptor for layouts of system RAM pieces
|
||||
* @family: Specifies the family being described
|
||||
* @n: Number of &struct mem_layout elements
|
||||
* @layout: Pointer to the list of &mem_layout structures
|
||||
*/
|
||||
struct mem_layout_list {
|
||||
enum family_type family;
|
||||
size_t n;
|
||||
struct mem_layout *layout;
|
||||
};
|
||||
|
||||
static struct mem_layout f1500_layout[] = {
|
||||
{0x20000000, 0x10000000, MEBIBYTE(256)},
|
||||
};
|
||||
|
||||
static struct mem_layout f4500_layout[] = {
|
||||
{0x40000000, 0x10000000, MEBIBYTE(256)},
|
||||
{0x20000000, 0x20000000, MEBIBYTE(32)},
|
||||
};
|
||||
|
||||
static struct mem_layout f8500_layout[] = {
|
||||
{0x40000000, 0x10000000, MEBIBYTE(256)},
|
||||
{0x20000000, 0x20000000, MEBIBYTE(32)},
|
||||
{0x30000000, 0x30000000, MEBIBYTE(32)},
|
||||
};
|
||||
|
||||
static struct mem_layout fx600_layout[] = {
|
||||
{0x20000000, 0x10000000, MEBIBYTE(256)},
|
||||
{0x60000000, 0x60000000, MEBIBYTE(128)},
|
||||
};
|
||||
|
||||
static struct mem_layout_list layout_list[] = {
|
||||
{FAMILY_1500, ARRAY_SIZE(f1500_layout), f1500_layout},
|
||||
{FAMILY_1500VZE, ARRAY_SIZE(f1500_layout), f1500_layout},
|
||||
{FAMILY_1500VZF, ARRAY_SIZE(f1500_layout), f1500_layout},
|
||||
{FAMILY_4500, ARRAY_SIZE(f4500_layout), f4500_layout},
|
||||
{FAMILY_8500, ARRAY_SIZE(f8500_layout), f8500_layout},
|
||||
{FAMILY_8500RNG, ARRAY_SIZE(f8500_layout), f8500_layout},
|
||||
{FAMILY_4600, ARRAY_SIZE(fx600_layout), fx600_layout},
|
||||
{FAMILY_4600VZA, ARRAY_SIZE(fx600_layout), fx600_layout},
|
||||
{FAMILY_8600, ARRAY_SIZE(fx600_layout), fx600_layout},
|
||||
{FAMILY_8600VZB, ARRAY_SIZE(fx600_layout), fx600_layout},
|
||||
};
|
||||
|
||||
/* If we can't determine the layout, use this */
|
||||
static struct mem_layout default_layout[] = {
|
||||
{0x20000000, 0x10000000, MEBIBYTE(128)},
|
||||
};
|
||||
|
||||
/**
|
||||
* register_non_ram - register low memory not available for RAM usage
|
||||
*/
|
||||
static __init void register_non_ram(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(low_mem_reserved); i++)
|
||||
add_memory_region(low_mem_reserved[i].start,
|
||||
low_mem_reserved[i].size, BOOT_MEM_RESERVED);
|
||||
}
|
||||
|
||||
/**
|
||||
* get_memsize - get the size of memory as a single bank
|
||||
*/
|
||||
static phys_addr_t get_memsize(void)
|
||||
{
|
||||
static char cmdline[COMMAND_LINE_SIZE] __initdata;
|
||||
phys_addr_t memsize = 0;
|
||||
char *memsize_str;
|
||||
unsigned long memsize = 0;
|
||||
unsigned int physend;
|
||||
char *ptr;
|
||||
int low_mem;
|
||||
int high_mem;
|
||||
|
||||
/* Check the command line first for a memsize directive */
|
||||
strcpy(cmdline, arcs_cmdline);
|
||||
|
@ -73,98 +186,158 @@ void __init prom_meminit(void)
|
|||
if (memsize == 0) {
|
||||
if (_prom_memsize != 0) {
|
||||
memsize = _prom_memsize;
|
||||
pr_info("_prom_memsize = 0x%lx\n", memsize);
|
||||
pr_info("_prom_memsize = 0x%x\n", memsize);
|
||||
/* add in memory that the bootloader doesn't
|
||||
* report */
|
||||
memsize += BOOT_MEM_SIZE;
|
||||
memsize += BLDR_SIZE;
|
||||
} else {
|
||||
memsize = DEFAULT_MEMSIZE;
|
||||
pr_info("Memsize not passed by bootloader, "
|
||||
"defaulting to 0x%lx\n", memsize);
|
||||
"defaulting to 0x%x\n", memsize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
physend = PFN_ALIGN(&_end) - 0x80000000;
|
||||
if (memsize > LOW_MEM_MAX) {
|
||||
low_mem = LOW_MEM_MAX;
|
||||
high_mem = memsize - low_mem;
|
||||
} else {
|
||||
low_mem = memsize;
|
||||
high_mem = 0;
|
||||
return memsize;
|
||||
}
|
||||
|
||||
/**
|
||||
* register_low_ram - register an aliased section of RAM
|
||||
* @p: Alias address of memory
|
||||
* @n: Number of bytes in this section of memory
|
||||
*
|
||||
* Returns the number of bytes registered
|
||||
*
|
||||
*/
|
||||
static __init phys_addr_t register_low_ram(phys_addr_t p, phys_addr_t n)
|
||||
{
|
||||
phys_addr_t s;
|
||||
int i;
|
||||
phys_addr_t orig_n;
|
||||
|
||||
orig_n = n;
|
||||
|
||||
BUG_ON(p + n > RV_PHYS);
|
||||
|
||||
for (i = 0; n != 0 && i < ARRAY_SIZE(low_mem_reserved); i++) {
|
||||
phys_addr_t start;
|
||||
phys_addr_t size;
|
||||
|
||||
start = low_mem_reserved[i].start;
|
||||
size = low_mem_reserved[i].size;
|
||||
|
||||
/* Handle memory before this low memory section */
|
||||
if (p < start) {
|
||||
phys_addr_t s;
|
||||
s = min(n, start - p);
|
||||
add_memory_region(p, s, BOOT_MEM_RAM);
|
||||
p += s;
|
||||
n -= s;
|
||||
}
|
||||
|
||||
/* Handle the low memory section itself. If it's aliased,
|
||||
* we reduce the number of byes left, but if not, the RAM
|
||||
* is available elsewhere and we don't reduce the number of
|
||||
* bytes remaining. */
|
||||
if (p == start) {
|
||||
if (low_mem_reserved[i].is_aliased) {
|
||||
s = min(n, size);
|
||||
n -= s;
|
||||
p += s;
|
||||
} else
|
||||
p += n;
|
||||
}
|
||||
}
|
||||
|
||||
return orig_n - n;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: We will use the hard code for memory configuration until
|
||||
* the bootloader releases their device tree to us.
|
||||
* register_ram - register real RAM
|
||||
* @p: Address of memory as seen by devices
|
||||
* @alias: If the memory is seen at an additional address by the processor,
|
||||
* this will be the address, otherwise it is the same as @p.
|
||||
* @n: Number of bytes in this section of memory
|
||||
*/
|
||||
static __init void register_ram(phys_addr_t p, phys_addr_t alias,
|
||||
phys_addr_t n)
|
||||
{
|
||||
/*
|
||||
* Add the memory reserved for use by the bootloader to the
|
||||
* memory map.
|
||||
* If some or all of this memory has an alias, break it into the
|
||||
* aliased and non-aliased portion.
|
||||
*/
|
||||
add_memory_region(PHYS_MEM_START, RES_BOOTLDR_MEMSIZE,
|
||||
BOOT_MEM_RESERVED);
|
||||
#ifdef CONFIG_HIGHMEM_256_128
|
||||
/*
|
||||
* Add memory in low for general use by the kernel and its friends
|
||||
* (like drivers, applications, etc).
|
||||
*/
|
||||
add_memory_region(PHYS_MEM_START + RES_BOOTLDR_MEMSIZE,
|
||||
LOW_MEM_MAX - RES_BOOTLDR_MEMSIZE, BOOT_MEM_RAM);
|
||||
/*
|
||||
* Add the memory reserved for reset vector.
|
||||
*/
|
||||
add_memory_region(0x1fc00000, MEBIBYTE(4), BOOT_MEM_RESERVED);
|
||||
/*
|
||||
* Add the memory reserved.
|
||||
*/
|
||||
add_memory_region(0x20000000, MEBIBYTE(1024 + 75), BOOT_MEM_RESERVED);
|
||||
/*
|
||||
* Add memory in high for general use by the kernel and its friends
|
||||
* (like drivers, applications, etc).
|
||||
*
|
||||
* 75MB is reserved for devices which are using the memory in high.
|
||||
*/
|
||||
add_memory_region(0x60000000 + MEBIBYTE(75), MEBIBYTE(128 - 75),
|
||||
BOOT_MEM_RAM);
|
||||
#elif defined CONFIG_HIGHMEM_128_128
|
||||
/*
|
||||
* Add memory in low for general use by the kernel and its friends
|
||||
* (like drivers, applications, etc).
|
||||
*/
|
||||
add_memory_region(PHYS_MEM_START + RES_BOOTLDR_MEMSIZE,
|
||||
MEBIBYTE(128) - RES_BOOTLDR_MEMSIZE, BOOT_MEM_RAM);
|
||||
/*
|
||||
* Add the memory reserved.
|
||||
*/
|
||||
add_memory_region(PHYS_MEM_START + MEBIBYTE(128),
|
||||
MEBIBYTE(128 + 1024 + 75), BOOT_MEM_RESERVED);
|
||||
/*
|
||||
* Add memory in high for general use by the kernel and its friends
|
||||
* (like drivers, applications, etc).
|
||||
*
|
||||
* 75MB is reserved for devices which are using the memory in high.
|
||||
*/
|
||||
add_memory_region(0x60000000 + MEBIBYTE(75), MEBIBYTE(128 - 75),
|
||||
BOOT_MEM_RAM);
|
||||
#else
|
||||
/* Add low memory regions for either:
|
||||
* - no-highmemory configuration case -OR-
|
||||
* - highmemory "HIGHMEM_LOWBANK_ONLY" case
|
||||
*/
|
||||
/*
|
||||
* Add memory for general use by the kernel and its friends
|
||||
* (like drivers, applications, etc).
|
||||
*/
|
||||
add_memory_region(PHYS_MEM_START + RES_BOOTLDR_MEMSIZE,
|
||||
low_mem - RES_BOOTLDR_MEMSIZE, BOOT_MEM_RAM);
|
||||
/*
|
||||
* Add the memory reserved for reset vector.
|
||||
*/
|
||||
add_memory_region(0x1fc00000, MEBIBYTE(4), BOOT_MEM_RESERVED);
|
||||
if (p != alias) {
|
||||
phys_addr_t alias_size;
|
||||
phys_addr_t registered;
|
||||
|
||||
alias_size = min(n, LOW_RAM_END - alias);
|
||||
registered = register_low_ram(alias, alias_size);
|
||||
ioremap_add_map(alias, p, n);
|
||||
n -= registered;
|
||||
p += registered;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HIGHMEM
|
||||
if (n != 0) {
|
||||
add_memory_region(p, n, BOOT_MEM_RAM);
|
||||
ioremap_add_map(p, p, n);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* register_address_space - register things in the address space
|
||||
* @memsize: Number of bytes of RAM installed
|
||||
*
|
||||
* Takes the given number of bytes of RAM and registers as many of the regions,
|
||||
* or partial regions, as it can. So, the default configuration might have
|
||||
* two regions with 256 MiB each. If the memsize passed in on the command line
|
||||
* is 384 MiB, it will register the first region with 256 MiB and the second
|
||||
* with 128 MiB.
|
||||
*/
|
||||
static __init void register_address_space(phys_addr_t memsize)
|
||||
{
|
||||
int i;
|
||||
phys_addr_t size;
|
||||
size_t n;
|
||||
struct mem_layout *layout;
|
||||
enum family_type family;
|
||||
|
||||
/*
|
||||
* Register all of the things that aren't available to the kernel as
|
||||
* memory.
|
||||
*/
|
||||
register_non_ram();
|
||||
|
||||
/* Find the appropriate memory description */
|
||||
family = platform_get_family();
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(layout_list); i++) {
|
||||
if (layout_list[i].family == family)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(layout_list)) {
|
||||
n = ARRAY_SIZE(default_layout);
|
||||
layout = default_layout;
|
||||
} else {
|
||||
n = layout_list[i].n;
|
||||
layout = layout_list[i].layout;
|
||||
}
|
||||
|
||||
for (i = 0; memsize != 0 && i < n; i++) {
|
||||
size = min(memsize, layout[i].size);
|
||||
register_ram(layout[i].phys, layout[i].alias, size);
|
||||
memsize -= size;
|
||||
}
|
||||
}
|
||||
|
||||
void __init prom_meminit(void)
|
||||
{
|
||||
ptv_memsize = get_memsize();
|
||||
register_address_space(ptv_memsize);
|
||||
}
|
||||
|
||||
void __init prom_free_prom_memory(void)
|
||||
{
|
||||
unsigned long addr;
|
||||
|
|
Loading…
Reference in a new issue