/*1* arch/sh/mm/tlb-sh5.c2*3* Copyright (C) 2003 Paul Mundt <[email protected]>4* Copyright (C) 2003 Richard Curnow <[email protected]>5*6* This file is subject to the terms and conditions of the GNU General Public7* License. See the file "COPYING" in the main directory of this archive8* for more details.9*/10#include <linux/mm.h>11#include <linux/init.h>12#include <asm/page.h>13#include <asm/tlb.h>14#include <asm/mmu_context.h>1516/**17* sh64_tlb_init - Perform initial setup for the DTLB and ITLB.18*/19int __init sh64_tlb_init(void)20{21/* Assign some sane DTLB defaults */22cpu_data->dtlb.entries = 64;23cpu_data->dtlb.step = 0x10;2425cpu_data->dtlb.first = DTLB_FIXED | cpu_data->dtlb.step;26cpu_data->dtlb.next = cpu_data->dtlb.first;2728cpu_data->dtlb.last = DTLB_FIXED |29((cpu_data->dtlb.entries - 1) *30cpu_data->dtlb.step);3132/* And again for the ITLB */33cpu_data->itlb.entries = 64;34cpu_data->itlb.step = 0x10;3536cpu_data->itlb.first = ITLB_FIXED | cpu_data->itlb.step;37cpu_data->itlb.next = cpu_data->itlb.first;38cpu_data->itlb.last = ITLB_FIXED |39((cpu_data->itlb.entries - 1) *40cpu_data->itlb.step);4142return 0;43}4445/**46* sh64_next_free_dtlb_entry - Find the next available DTLB entry47*/48unsigned long long sh64_next_free_dtlb_entry(void)49{50return cpu_data->dtlb.next;51}5253/**54* sh64_get_wired_dtlb_entry - Allocate a wired (locked-in) entry in the DTLB55*/56unsigned long long sh64_get_wired_dtlb_entry(void)57{58unsigned long long entry = sh64_next_free_dtlb_entry();5960cpu_data->dtlb.first += cpu_data->dtlb.step;61cpu_data->dtlb.next += cpu_data->dtlb.step;6263return entry;64}6566/**67* sh64_put_wired_dtlb_entry - Free a wired (locked-in) entry in the DTLB.68*69* @entry: Address of TLB slot.70*71* Works like a stack, last one to allocate must be first one to free.72*/73int sh64_put_wired_dtlb_entry(unsigned long long entry)74{75__flush_tlb_slot(entry);7677/*78* We don't do any particularly useful tracking of wired entries,79* so this approach works like a stack .. last one to be allocated80* has to be the first one to be freed.81*82* We could potentially load wired entries into a list and work on83* rebalancing the list periodically (which also entails moving the84* contents of a TLB entry) .. though I have a feeling that this is85* more trouble than it's worth.86*/8788/*89* Entry must be valid .. we don't want any ITLB addresses!90*/91if (entry <= DTLB_FIXED)92return -EINVAL;9394/*95* Next, check if we're within range to be freed. (ie, must be the96* entry beneath the first 'free' entry!97*/98if (entry < (cpu_data->dtlb.first - cpu_data->dtlb.step))99return -EINVAL;100101/* If we are, then bring this entry back into the list */102cpu_data->dtlb.first -= cpu_data->dtlb.step;103cpu_data->dtlb.next = entry;104105return 0;106}107108/**109* sh64_setup_tlb_slot - Load up a translation in a wired slot.110*111* @config_addr: Address of TLB slot.112* @eaddr: Virtual address.113* @asid: Address Space Identifier.114* @paddr: Physical address.115*116* Load up a virtual<->physical translation for @eaddr<->@paddr in the117* pre-allocated TLB slot @config_addr (see sh64_get_wired_dtlb_entry).118*/119void sh64_setup_tlb_slot(unsigned long long config_addr, unsigned long eaddr,120unsigned long asid, unsigned long paddr)121{122unsigned long long pteh, ptel;123124pteh = neff_sign_extend(eaddr);125pteh &= PAGE_MASK;126pteh |= (asid << PTEH_ASID_SHIFT) | PTEH_VALID;127ptel = neff_sign_extend(paddr);128ptel &= PAGE_MASK;129ptel |= (_PAGE_CACHABLE | _PAGE_READ | _PAGE_WRITE);130131asm volatile("putcfg %0, 1, %1\n\t"132"putcfg %0, 0, %2\n"133: : "r" (config_addr), "r" (ptel), "r" (pteh));134}135136/**137* sh64_teardown_tlb_slot - Teardown a translation.138*139* @config_addr: Address of TLB slot.140*141* Teardown any existing mapping in the TLB slot @config_addr.142*/143void sh64_teardown_tlb_slot(unsigned long long config_addr)144__attribute__ ((alias("__flush_tlb_slot")));145146static int dtlb_entry;147static unsigned long long dtlb_entries[64];148149void tlb_wire_entry(struct vm_area_struct *vma, unsigned long addr, pte_t pte)150{151unsigned long long entry;152unsigned long paddr, flags;153154BUG_ON(dtlb_entry == ARRAY_SIZE(dtlb_entries));155156local_irq_save(flags);157158entry = sh64_get_wired_dtlb_entry();159dtlb_entries[dtlb_entry++] = entry;160161paddr = pte_val(pte) & _PAGE_FLAGS_HARDWARE_MASK;162paddr &= ~PAGE_MASK;163164sh64_setup_tlb_slot(entry, addr, get_asid(), paddr);165166local_irq_restore(flags);167}168169void tlb_unwire_entry(void)170{171unsigned long long entry;172unsigned long flags;173174BUG_ON(!dtlb_entry);175176local_irq_save(flags);177entry = dtlb_entries[dtlb_entry--];178179sh64_teardown_tlb_slot(entry);180sh64_put_wired_dtlb_entry(entry);181182local_irq_restore(flags);183}184185186