fsl_booke_mmu.c 5.9 KB
Newer Older
1
/*
2
 * Modifications by Kumar Gala (galak@kernel.crashing.org) to support
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
 * E500 Book E processors.
 *
 * Copyright 2004 Freescale Semiconductor, Inc
 *
 * This file contains the routines for initializing the MMU
 * on the 4xx series of chips.
 *  -- paulus
 *
 *  Derived from arch/ppc/mm/init.c:
 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
 *
 *  Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au)
 *  and Cort Dougan (PReP) (cort@cs.nmt.edu)
 *    Copyright (C) 1996 Paul Mackerras
 *
 *  Derived from "arch/i386/mm/init.c"
 *    Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 *
 *  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 <linux/signal.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/ptrace.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/stddef.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/highmem.h>

#include <asm/pgalloc.h>
#include <asm/prom.h>
#include <asm/io.h>
#include <asm/mmu_context.h>
#include <asm/pgtable.h>
#include <asm/mmu.h>
#include <asm/uaccess.h>
#include <asm/smp.h>
#include <asm/machdep.h>
#include <asm/setup.h>

55 56
#include "mmu_decl.h"

57 58
extern void loadcam_entry(unsigned int index);
unsigned int tlbcam_index;
59
static unsigned long cam[CONFIG_LOWMEM_CAM_NUM];
60 61 62

#define NUM_TLBCAMS	(16)

63 64 65 66
#if defined(CONFIG_LOWMEM_CAM_NUM_BOOL) && (CONFIG_LOWMEM_CAM_NUM >= NUM_TLBCAMS)
#error "LOWMEM_CAM_NUM must be less than NUM_TLBCAMS"
#endif

67
struct tlbcam TLBCAM[NUM_TLBCAMS];
68 69 70 71 72 73 74 75 76 77 78 79

struct tlbcamrange {
   	unsigned long start;
	unsigned long limit;
	phys_addr_t phys;
} tlbcam_addrs[NUM_TLBCAMS];

extern unsigned int tlbcam_index;

/*
 * Return PA for this VA if it is mapped by a CAM, or 0
 */
80
phys_addr_t v_mapped_by_tlbcam(unsigned long va)
81 82 83 84 85 86 87 88 89 90 91
{
	int b;
	for (b = 0; b < tlbcam_index; ++b)
		if (va >= tlbcam_addrs[b].start && va < tlbcam_addrs[b].limit)
			return tlbcam_addrs[b].phys + (va - tlbcam_addrs[b].start);
	return 0;
}

/*
 * Return VA for a given PA or 0 if not mapped
 */
92
unsigned long p_mapped_by_tlbcam(phys_addr_t pa)
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
{
	int b;
	for (b = 0; b < tlbcam_index; ++b)
		if (pa >= tlbcam_addrs[b].phys
	    	    && pa < (tlbcam_addrs[b].limit-tlbcam_addrs[b].start)
		              +tlbcam_addrs[b].phys)
			return tlbcam_addrs[b].start+(pa-tlbcam_addrs[b].phys);
	return 0;
}

/*
 * Set up one of the I/D BAT (block address translation) register pairs.
 * The parameters are not checked; in particular size must be a power
 * of 4 between 4k and 256M.
 */
void settlbcam(int index, unsigned long virt, phys_addr_t phys,
		unsigned int size, int flags, unsigned int pid)
{
	unsigned int tsize, lz;

	asm ("cntlzw %0,%1" : "=r" (lz) : "r" (size));
114
	tsize = 21 - lz;
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158

#ifdef CONFIG_SMP
	if ((flags & _PAGE_NO_CACHE) == 0)
		flags |= _PAGE_COHERENT;
#endif

	TLBCAM[index].MAS0 = MAS0_TLBSEL(1) | MAS0_ESEL(index) | MAS0_NV(index+1);
	TLBCAM[index].MAS1 = MAS1_VALID | MAS1_IPROT | MAS1_TSIZE(tsize) | MAS1_TID(pid);
	TLBCAM[index].MAS2 = virt & PAGE_MASK;

	TLBCAM[index].MAS2 |= (flags & _PAGE_WRITETHRU) ? MAS2_W : 0;
	TLBCAM[index].MAS2 |= (flags & _PAGE_NO_CACHE) ? MAS2_I : 0;
	TLBCAM[index].MAS2 |= (flags & _PAGE_COHERENT) ? MAS2_M : 0;
	TLBCAM[index].MAS2 |= (flags & _PAGE_GUARDED) ? MAS2_G : 0;
	TLBCAM[index].MAS2 |= (flags & _PAGE_ENDIAN) ? MAS2_E : 0;

	TLBCAM[index].MAS3 = (phys & PAGE_MASK) | MAS3_SX | MAS3_SR;
	TLBCAM[index].MAS3 |= ((flags & _PAGE_RW) ? MAS3_SW : 0);

#ifndef CONFIG_KGDB /* want user access for breakpoints */
	if (flags & _PAGE_USER) {
	   TLBCAM[index].MAS3 |= MAS3_UX | MAS3_UR;
	   TLBCAM[index].MAS3 |= ((flags & _PAGE_RW) ? MAS3_UW : 0);
	}
#else
	TLBCAM[index].MAS3 |= MAS3_UX | MAS3_UR;
	TLBCAM[index].MAS3 |= ((flags & _PAGE_RW) ? MAS3_UW : 0);
#endif

	tlbcam_addrs[index].start = virt;
	tlbcam_addrs[index].limit = virt + size - 1;
	tlbcam_addrs[index].phys = phys;

	loadcam_entry(index);
}

void invalidate_tlbcam_entry(int index)
{
	TLBCAM[index].MAS0 = MAS0_TLBSEL(1) | MAS0_ESEL(index);
	TLBCAM[index].MAS1 = ~MAS1_VALID;

	loadcam_entry(index);
}

159
unsigned long __init mmu_mapin_ram(void)
160
{
161 162 163 164 165 166 167
	unsigned long virt = PAGE_OFFSET;
	phys_addr_t phys = memstart_addr;

	while (cam[tlbcam_index] && tlbcam_index < ARRAY_SIZE(cam)) {
		settlbcam(tlbcam_index, virt, phys, cam[tlbcam_index], _PAGE_KERNEL, 0);
		virt += cam[tlbcam_index];
		phys += cam[tlbcam_index];
168 169
		tlbcam_index++;
	}
170 171

	return virt - PAGE_OFFSET;
172 173 174 175 176 177 178 179 180 181 182 183 184
}

/*
 * MMU_init_hw does the chip-specific initialization of the MMU hardware.
 */
void __init MMU_init_hw(void)
{
	flush_instruction_cache();
}

void __init
adjust_total_lowmem(void)
{
185
	phys_addr_t ram;
186
	unsigned int max_cam = (mfspr(SPRN_TLB1CFG) >> 16) & 0xff;
187 188
	char buf[ARRAY_SIZE(cam) * 5 + 1], *p = buf;
	int i;
189 190
	unsigned long virt = PAGE_OFFSET & 0xffffffffUL;
	unsigned long phys = memstart_addr & 0xffffffffUL;
191

192 193
	/* Convert (4^max) kB to (2^max) bytes */
	max_cam = max_cam * 2 + 10;
194

195 196
	/* adjust lowmem size to __max_low_memory */
	ram = min((phys_addr_t)__max_low_memory, (phys_addr_t)total_lowmem);
197 198

	/* Calculate CAM values */
199 200 201
	__max_low_memory = 0;
	for (i = 0; ram && i < ARRAY_SIZE(cam); i++) {
		unsigned int camsize = __ilog2(ram) & ~1U;
202 203 204 205
		unsigned int align = __ffs(virt | phys) & ~1U;

		if (camsize > align)
			camsize = align;
206 207
		if (camsize > max_cam)
			camsize = max_cam;
208

209 210 211
		cam[i] = 1UL << camsize;
		ram -= cam[i];
		__max_low_memory += cam[i];
212 213
		virt += cam[i];
		phys += cam[i];
214 215

		p += sprintf(p, "%lu/", cam[i] >> 20);
216
	}
217 218 219
	for (; i < ARRAY_SIZE(cam); i++)
		p += sprintf(p, "0/");
	p[-1] = '\0';
220

221 222
	pr_info("Memory CAM mapping: %s Mb, residual: %ldMb\n", buf,
	        (total_lowmem - __max_low_memory) >> 20);
223
	__initial_memory_limit_addr = memstart_addr + __max_low_memory;
224
}