📄 lh7a400_cp15_driver.c
字号:
/**********************************************************************
* $Workfile: LH7A400_cp15_driver.c $
* $Revision: 1.3 $
* $Author: BarnettH $
* $Date: Jun 19 2002 09:39:44 $
*
* Project: LH7A400
*
* Description:
* This module contains utilities for controlling coprocessor 15
* MMU and cache functions for the LH7A400
*
* References:
* ARM Architecture Reference Manual (ARM DDI 0100E)
* ARM 922 (Rev 0) Technical Reference Manual (ARM DDI 0184A)
*
* Revision History:
* $Log: //smaicnt2/pvcs/VM/CHIPS/archives/LH7A400/MMU/Drivers/LH7A400_cp15_driver.c-arc $
*
* Rev 1.3 Jun 19 2002 09:39:44 BarnettH
* Changed identifier 'virtual' to 'virtual_addr' to eliminate conflict with C++ keyword.
*
* Rev 1.2 Jan 30 2002 08:31:58 KovitzP
* corrected bug in TLB base address mask
*
* Rev 1.1 Jan 09 2002 09:32:24 KovitzP
* Added LH7A400_ prefix to function names
*
* Rev 1.0 Jan 07 2002 11:13:12 KovitzP
* Initial revision.
*
*
* COPYRIGHT (C) 2001 SHARP MICROELECTRONICS OF THE AMERICAS, INC.
* CAMAS, WA
*********************************************************************/
#include "SMA_types.h"
#include "LH7A400_cp15_driver.h"
#ifndef NULL
#define NULL 0
#endif
static UNS_32 decode_level2(UNS_32 level2, UNS_32 addr);
/**********************************************************************
*
* Function: LH7A400_cp15_map_virtual_to_physical
*
* Purpose:
* given a virtual address, return a
* UNS_32 containing the physical address
*
* Processing:
* return (UNS_32)addr if MMU is turned off. Otherwise, read the
* address of the translation table from the translation table base
* address register. Use the upper 12 bits of the addr to index the
* translation table and read out the descriptor. If the descriptor is
* invalid, return 0. If the descriptor is for a 1 Meg section, read back
* the upper 12 bits of the physical address. The lower 20 bits of the
* physical address is the lower 20 bits of the virtual address. If
* the descriptor is for a coarse page table, read the coarse page
* table descriptor and use the most significant 22 bits as the base
* address of the page table. If the descriptor is for a fine page
* table, read the fine page table descriptor and use the most
* significant 20 bits as the base address of the page table.
*
* If not a section base, read the level 2 page descriptor from the page
* table. If bits 1..0 of the level2 descriptor are 01, then it is a
* large page table descriptor. The most significant 16 bits of the
* descriptor are the most significant 16 bits of the physical address;
* the least significant 16-bits of the virtual address are the least
* significant 16-bits of the address. If bits 1..0 of the level2
* descriptor are 10, then it is a small page table descriptor. The most
* significant 20 bits of the level2 descriptor are the most significant
* 20 bits of the physical address; the least significant 12 bits are
* the least significant 12 bits of the physical address. If bits 1..0 of
* the level2 descriptor are 11, then it is a tiny page table descriptor.
* The most significant 22 bits of the level2 descriptor are the most
* significant 22 bits of the physical address; the least significant
* 10 bits are the least significant 10 bits of the physical address. If
* bits 1..0 of the level2 descriptor are 0, return 0 (invalid).
*
* Parameters:
* addr: the virtual address to be converted
*
* Outputs: None
*
* Returns:
* the physical address or 0 if the address does not translate.
*
* Notes:
* CAUTION: This routine counts on the physical address and the
* virtual address of the translation table being the same. If
* they are not the same, then the step that reads the translation
* table base register won't initialize the tlb variable properly.
* If page tables are used, then the virtual addresses of the page
* tables must be the same as the physical addresses or else the page
* table read will return an invalid value.
*
**********************************************************************/
UNS_32 LH7A400_cp15_map_virtual_to_physical(void * addr)
{
register UNS_32 tlb_val;
register UNS_32 status;
UNS_32 * tlb, tlb_entry, index, virtual_addr;
UNS_32 * page_table, level2;
virtual_addr = (UNS_32)addr;
/* read the control register and verify that MMU is on */
__asm
{
MRC p15,0,status,c1,c0,0;
}
if ((status & _BIT(0)) == 0) /* bit 0 is MMU enable bit */
{
/* then MMU is off; virtual is physical */
return virtual_addr;
}
/* read the translation table base register */
__asm
{
MRC p15,0,tlb_val,c2,c0,0;
}
/* mask off unpredictable bits */
tlb = (UNS_32 *)(tlb_val & _SBF(14,_BITMASK(18)) );
/*
get the level 1 translation table entry
indexed by bits 31:20 of the address
*/
index = virtual_addr;
index >>= 20;
tlb_entry = tlb[index];
/* figure out how to use the tlb entry based on the lower two bits */
switch (tlb_entry & _BITMASK(2))
{
case 0:
/* invalid */
return 0;
break;
case 1:
/* course page tables */
index = (virtual_addr >> 12) & _BITMASK(8);
page_table = (UNS_32 *)(tlb_entry & _SBF(10, _BITMASK(22)) );
level2 = page_table[index];
break;
case 2:
/*
section base -- upper 12 bits of entry is physical memory base
lower 20 bits of virtual address is offset from that base
*/
return (tlb_entry & _SBF(20,_BITMASK(12)) ) |
(virtual_addr & _BITMASK(20) );
break;
case 3:
/* fine page tables */
index = (virtual_addr >> 10) & _BITMASK(10);
page_table = (UNS_32 *)(tlb_entry & _SBF(12, _BITMASK(20)) );
level2 = page_table[index];
break;
}
switch (level2 & _BITMASK(2) )
{
case 1:
/* large page table */
return (level2 & _SBF(16, _BITMASK(16)) ) |
(virtual_addr & _BITMASK(16) );
case 2:
/* small page table */
return (level2 & _SBF(12, _BITMASK(20)) ) |
(virtual_addr & _BITMASK(12) );
case 3:
return (level2 & _SBF(10, _BITMASK(22)) ) |
(virtual_addr & _BITMASK(10) );
}
/* this line should not execute */
return 0;
}
/**********************************************************************
*
* Function: LH7A400_cp15_map_physical_to_virtual
*
* Purpose:
* given a physical address, return a
* void pointer containing the virtual address
*
* Processing:
*
* Parameters:
* addr: the physical address to be converted
*
* Outputs: None
*
* Returns:
* the virtual address or NULL if the address does not translate.
*
* Notes:
* CAUTION: This routine counts on the physical address and the
* virtual address of the translation table being the same. If
* they are not the same, then the step that reads the translation
* table base register won't initialize the tlb variable properly.
* If page tables are used, then the virtual addresses of the page
* tables must be the same as the physical addresses or else the page
* table read will return an invalid value.
*
**********************************************************************/
void * LH7A400_cp15_map_physical_to_virtual(UNS_32 addr)
{
register UNS_32 tlb_val;
register UNS_32 status;
UNS_32 * tlb, tlb_entry, index;
UNS_32 * page_table, level2, index2;
void * virtual_addr;
/*
do a linear search of the translation table until the
level 1 descriptor corresponding to the physical address is found
*/
/* read the control register and verify that MMU is on */
__asm
{
MRC p15,0,status,c1,c0,0;
}
if ((status & _BIT(0)) == 0) /* bit 0 is MMU enable bit */
{
/* then MMU is off; virtual is physical */
return (void *)addr;
}
/* read the translation table base register */
__asm
{
MRC p15,0,tlb_val,c2,c0,0;
}
/* mask off unpredictable bits */
tlb = (UNS_32 *)(tlb_val & _SBF(14,_BITMASK(18)) );
/*
search until found or all 4096 translation
table entries are examined.
*/
for (index = 0; index < 4096; index ++)
{
tlb_entry = tlb[index];
switch (tlb_entry & _BITMASK(3))
{
case 0:
/* invalid */
break;
case 1:
/* course page tables */
for (index2 = 0; index2 < 256; index2++)
{
page_table = (UNS_32 *)(tlb_entry & _SBF(10, _BITMASK(22)) );
level2 = page_table[index2];
if (level2)
{
virtual_addr = (void *)decode_level2(level2, addr);
if (virtual_addr)
return virtual_addr;
}
break;
}
case 2:
/*
section base -- upper 12 bits of entry is physical memory base
lower 20 bits of virtual address is offset from that base
*/
if ( (tlb_entry & _SBF(20,_BITMASK(12)))
== (addr & _SBF(20,_BITMASK(12))) )
{
return (void *)( _SBF(20,index) | (addr & _BITMASK(20)) );
}
break;
case 3:
/* fine page tables */
for (index2 = 0; index2 < 256; index2++)
{
page_table = (UNS_32 *)(tlb_entry & _SBF(12, _BITMASK(20)) );
level2 = page_table[index2];
if (level2)
{
virtual_addr = (void *)decode_level2(level2, addr);
if (virtual_addr)
return virtual_addr;
}
}
break;
}
}
/* virtual address not found */
return NULL;
}
/**********************************************************************
*
* Function: decode_level2
*
* Purpose:
* given a level2 descriptor and a physical address, return
* the virtual address if the level2 descriptor can map the
* physical address. Otherwise,
*
* Processing:
*
* Parameters:
* addr: the physical address to be converted
*
* Outputs: None
*
* Returns:
* the virtual address or NULL if the address does not translate.
*
* Notes:
* CAUTION: This routine counts on the physical address and the
* virtual address of the translation table being the same. If
* they are not the same, then the step that reads the translation
* table base register won't initialize the tlb variable properly.
* If page tables are used, then the virtual addresses of the page
* tables must be the same as the physical addresses or else the page
* table read will return an invalid value.
*
**********************************************************************/
static UNS_32 decode_level2(UNS_32 level2, UNS_32 addr)
{
switch (level2 & _BITMASK(2) )
{
case 1:
/* large page table */
if ( (level2 & _SBF(16, _BITMASK(16))) ==
(addr & _SBF(16, _BITMASK(16))) )
return (level2 & _SBF(16, _BITMASK(16)) ) |
(addr & _BITMASK(16) );
case 2:
/* small page table */
if ( (level2 & _SBF(12, _BITMASK(20))) ==
(addr & _SBF(12, _BITMASK(20))) )
return (level2 & _SBF(12, _BITMASK(20)) ) |
(addr & _BITMASK(12) );
case 3:
if ( (level2 & _SBF(10, _BITMASK(22))) ==
(addr & _SBF(10, _BITMASK(22))) )
return (level2 & _SBF(10, _BITMASK(22)) ) |
(addr & _BITMASK(10) );
}
return 0;
}
/**********************************************************************
*
* Function: LH7A400_force_cache_coherence
*
* Purpose:
* Force the CPU to recognize the block of code that was just written
* to memory between start_adr and end_adr even if caching and write
* buffering is on.
*
* Processing:
* cache lines are 32-bytes (8 words); clean and invalidate each
* line of D-cache and invalidate each line of I-cache within the
* address range.
*
* Invalidate the I-TLB within the the address range. The I-TLB has
* 256 word granularity.
*
* Parameters:
* start_adr: The first address in the code block
* end_adr: The last address in the code block
*
* Outputs: None
*
* Returns: Nothing
*
* Notes: None
*
**********************************************************************/
void LH7A400_force_cache_coherence(UNS_32 * const start_adr,
UNS_32 * const end_adr)
{
register UNS_32 * adr;
/*
cache lines are 32-bytes (8 words); clean and invalidate each
line of D-cache and invalidate each line of I-cache within the
address range.
*/
for (adr = (UNS_32 *)((UNS_32)start_adr & _SBF(2,_BITMASK(30)) );
adr < end_adr;
adr += 8)
{
/* p15 is MMU coprocessor, Cache OPS is c7, TLB OPS is c8 */
__asm
{
MOV r0,adr
/* Clean and Invalidate D-Cache single entry using MVA format */
MCR p15, 0, r0, c7, c14, 1
/* Invalidate I-Cache single entry using MVA format */
MCR p15, 0, r0, c7, c5, 1
}
}
/*
Invalidate the I-TLB within the the address range. The I-TLB has
256 word granularity.
*/
for (adr = (UNS_32 *)((UNS_32)start_adr & _SBF(10,_BITMASK(22)) );
adr < end_adr;
adr += 256)
{
__asm
{
MOV r0,adr
/* Invalidate I-TLB using MVA format */
MCR p15, 0, r0, c8, c5, 1
NOP
NOP
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -