| /* |
| * arch/xtensa/mm/mmu.c |
| * |
| * Logic that manipulates the Xtensa MMU. Derived from MIPS. |
| * |
| * 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) 2001 - 2003 Tensilica Inc. |
| * |
| * Joe Taylor |
| * Chris Zankel <chris@zankel.net> |
| * Marc Gauthier |
| */ |
| |
| #include <linux/mm.h> |
| #include <asm/processor.h> |
| #include <asm/mmu_context.h> |
| #include <asm/tlbflush.h> |
| #include <asm/system.h> |
| #include <asm/cacheflush.h> |
| |
| |
| static inline void __flush_itlb_all (void) |
| { |
| int way, index; |
| |
| for (way = 0; way < XCHAL_ITLB_ARF_WAYS; way++) { |
| for (index = 0; index < ITLB_ENTRIES_PER_ARF_WAY; index++) { |
| int entry = way + (index << PAGE_SHIFT); |
| invalidate_itlb_entry_no_isync (entry); |
| } |
| } |
| asm volatile ("isync\n"); |
| } |
| |
| static inline void __flush_dtlb_all (void) |
| { |
| int way, index; |
| |
| for (way = 0; way < XCHAL_DTLB_ARF_WAYS; way++) { |
| for (index = 0; index < DTLB_ENTRIES_PER_ARF_WAY; index++) { |
| int entry = way + (index << PAGE_SHIFT); |
| invalidate_dtlb_entry_no_isync (entry); |
| } |
| } |
| asm volatile ("isync\n"); |
| } |
| |
| |
| void flush_tlb_all (void) |
| { |
| __flush_itlb_all(); |
| __flush_dtlb_all(); |
| } |
| |
| /* If mm is current, we simply assign the current task a new ASID, thus, |
| * invalidating all previous tlb entries. If mm is someone else's user mapping, |
| * wie invalidate the context, thus, when that user mapping is swapped in, |
| * a new context will be assigned to it. |
| */ |
| |
| void flush_tlb_mm(struct mm_struct *mm) |
| { |
| #if 0 |
| printk("[tlbmm<%lx>]\n", (unsigned long)mm->context); |
| #endif |
| |
| if (mm == current->active_mm) { |
| int flags; |
| local_save_flags(flags); |
| get_new_mmu_context(mm, asid_cache); |
| set_rasid_register(ASID_INSERT(mm->context)); |
| local_irq_restore(flags); |
| } |
| else |
| mm->context = 0; |
| } |
| |
| void flush_tlb_range (struct vm_area_struct *vma, |
| unsigned long start, unsigned long end) |
| { |
| struct mm_struct *mm = vma->vm_mm; |
| unsigned long flags; |
| |
| if (mm->context == NO_CONTEXT) |
| return; |
| |
| #if 0 |
| printk("[tlbrange<%02lx,%08lx,%08lx>]\n", |
| (unsigned long)mm->context, start, end); |
| #endif |
| local_save_flags(flags); |
| |
| if (end-start + (PAGE_SIZE-1) <= SMALLEST_NTLB_ENTRIES << PAGE_SHIFT) { |
| int oldpid = get_rasid_register(); |
| set_rasid_register (ASID_INSERT(mm->context)); |
| start &= PAGE_MASK; |
| if (vma->vm_flags & VM_EXEC) |
| while(start < end) { |
| invalidate_itlb_mapping(start); |
| invalidate_dtlb_mapping(start); |
| start += PAGE_SIZE; |
| } |
| else |
| while(start < end) { |
| invalidate_dtlb_mapping(start); |
| start += PAGE_SIZE; |
| } |
| |
| set_rasid_register(oldpid); |
| } else { |
| get_new_mmu_context(mm, asid_cache); |
| if (mm == current->active_mm) |
| set_rasid_register(ASID_INSERT(mm->context)); |
| } |
| local_irq_restore(flags); |
| } |
| |
| void flush_tlb_page (struct vm_area_struct *vma, unsigned long page) |
| { |
| struct mm_struct* mm = vma->vm_mm; |
| unsigned long flags; |
| int oldpid; |
| #if 0 |
| printk("[tlbpage<%02lx,%08lx>]\n", |
| (unsigned long)mm->context, page); |
| #endif |
| |
| if(mm->context == NO_CONTEXT) |
| return; |
| |
| local_save_flags(flags); |
| |
| oldpid = get_rasid_register(); |
| |
| if (vma->vm_flags & VM_EXEC) |
| invalidate_itlb_mapping(page); |
| invalidate_dtlb_mapping(page); |
| |
| set_rasid_register(oldpid); |
| |
| local_irq_restore(flags); |
| |
| #if 0 |
| flush_tlb_all(); |
| return; |
| #endif |
| } |
| |
| |
| #ifdef DEBUG_TLB |
| |
| #define USE_ITLB 0 |
| #define USE_DTLB 1 |
| |
| struct way_config_t { |
| int indicies; |
| int indicies_log2; |
| int pgsz_log2; |
| int arf; |
| }; |
| |
| static struct way_config_t itlb[XCHAL_ITLB_WAYS] = |
| { |
| { XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ENTRIES), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ENTRIES_LOG2), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, PAGESZ_LOG2_MIN), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ARF) |
| }, |
| { XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ENTRIES), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ENTRIES_LOG2), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, PAGESZ_LOG2_MIN), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ARF) |
| }, |
| { XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ENTRIES), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ENTRIES_LOG2), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, PAGESZ_LOG2_MIN), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ARF) |
| }, |
| { XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ENTRIES), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ENTRIES_LOG2), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, PAGESZ_LOG2_MIN), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ARF) |
| }, |
| { XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ENTRIES), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ENTRIES_LOG2), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, PAGESZ_LOG2_MIN), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ARF) |
| }, |
| { XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ENTRIES), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ENTRIES_LOG2), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, PAGESZ_LOG2_MIN), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ARF) |
| }, |
| { XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ENTRIES), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ENTRIES_LOG2), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, PAGESZ_LOG2_MIN), |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ARF) |
| } |
| }; |
| |
| static struct way_config_t dtlb[XCHAL_DTLB_WAYS] = |
| { |
| { XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ENTRIES), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ENTRIES_LOG2), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, PAGESZ_LOG2_MIN), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ARF) |
| }, |
| { XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ENTRIES), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ENTRIES_LOG2), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, PAGESZ_LOG2_MIN), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ARF) |
| }, |
| { XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ENTRIES), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ENTRIES_LOG2), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, PAGESZ_LOG2_MIN), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ARF) |
| }, |
| { XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ENTRIES), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ENTRIES_LOG2), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, PAGESZ_LOG2_MIN), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ARF) |
| }, |
| { XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ENTRIES), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ENTRIES_LOG2), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, PAGESZ_LOG2_MIN), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ARF) |
| }, |
| { XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ENTRIES), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ENTRIES_LOG2), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, PAGESZ_LOG2_MIN), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ARF) |
| }, |
| { XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ENTRIES), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ENTRIES_LOG2), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, PAGESZ_LOG2_MIN), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ARF) |
| }, |
| { XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ENTRIES), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ENTRIES_LOG2), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, PAGESZ_LOG2_MIN), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ARF) |
| }, |
| { XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ENTRIES), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ENTRIES_LOG2), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, PAGESZ_LOG2_MIN), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ARF) |
| }, |
| { XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ENTRIES), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ENTRIES_LOG2), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, PAGESZ_LOG2_MIN), |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ARF) |
| } |
| }; |
| |
| /* Total number of entries: */ |
| #define ITLB_TOTAL_ENTRIES \ |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY0_SET, ENTRIES) + \ |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY1_SET, ENTRIES) + \ |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY2_SET, ENTRIES) + \ |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY3_SET, ENTRIES) + \ |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY4_SET, ENTRIES) + \ |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY5_SET, ENTRIES) + \ |
| XCHAL_ITLB_SET(XCHAL_ITLB_WAY6_SET, ENTRIES) |
| #define DTLB_TOTAL_ENTRIES \ |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY0_SET, ENTRIES) + \ |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY1_SET, ENTRIES) + \ |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY2_SET, ENTRIES) + \ |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY3_SET, ENTRIES) + \ |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY4_SET, ENTRIES) + \ |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY5_SET, ENTRIES) + \ |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY6_SET, ENTRIES) + \ |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY7_SET, ENTRIES) + \ |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY8_SET, ENTRIES) + \ |
| XCHAL_DTLB_SET(XCHAL_DTLB_WAY9_SET, ENTRIES) |
| |
| |
| typedef struct { |
| unsigned va; |
| unsigned pa; |
| unsigned char asid; |
| unsigned char ca; |
| unsigned char way; |
| unsigned char index; |
| unsigned char pgsz_log2; /* 0 .. 32 */ |
| unsigned char type; /* 0=ITLB 1=DTLB */ |
| } tlb_dump_entry_t; |
| |
| /* Return -1 if a precedes b, +1 if a follows b, 0 if same: */ |
| int cmp_tlb_dump_info( tlb_dump_entry_t *a, tlb_dump_entry_t *b ) |
| { |
| if (a->asid < b->asid) return -1; |
| if (a->asid > b->asid) return 1; |
| if (a->va < b->va) return -1; |
| if (a->va > b->va) return 1; |
| if (a->pa < b->pa) return -1; |
| if (a->pa > b->pa) return 1; |
| if (a->ca < b->ca) return -1; |
| if (a->ca > b->ca) return 1; |
| if (a->way < b->way) return -1; |
| if (a->way > b->way) return 1; |
| if (a->index < b->index) return -1; |
| if (a->index > b->index) return 1; |
| return 0; |
| } |
| |
| void sort_tlb_dump_info( tlb_dump_entry_t *t, int n ) |
| { |
| int i, j; |
| /* Simple O(n*n) sort: */ |
| for (i = 0; i < n-1; i++) |
| for (j = i+1; j < n; j++) |
| if (cmp_tlb_dump_info(t+i, t+j) > 0) { |
| tlb_dump_entry_t tmp = t[i]; |
| t[i] = t[j]; |
| t[j] = tmp; |
| } |
| } |
| |
| |
| static tlb_dump_entry_t itlb_dump_info[ITLB_TOTAL_ENTRIES]; |
| static tlb_dump_entry_t dtlb_dump_info[DTLB_TOTAL_ENTRIES]; |
| |
| |
| static inline char *way_type (int type) |
| { |
| return type ? "autorefill" : "non-autorefill"; |
| } |
| |
| void print_entry (struct way_config_t *way_info, |
| unsigned int way, |
| unsigned int index, |
| unsigned int virtual, |
| unsigned int translation) |
| { |
| char valid_chr; |
| unsigned int va, pa, asid, ca; |
| |
| va = virtual & |
| ~((1 << (way_info->pgsz_log2 + way_info->indicies_log2)) - 1); |
| asid = virtual & ((1 << XCHAL_MMU_ASID_BITS) - 1); |
| pa = translation & ~((1 << way_info->pgsz_log2) - 1); |
| ca = translation & ((1 << XCHAL_MMU_CA_BITS) - 1); |
| valid_chr = asid ? 'V' : 'I'; |
| |
| /* Compute and incorporate the effect of the index bits on the |
| * va. It's more useful for kernel debugging, since we always |
| * want to know the effective va anyway. */ |
| |
| va += index << way_info->pgsz_log2; |
| |
| printk ("\t[%d,%d] (%c) vpn 0x%.8x ppn 0x%.8x asid 0x%.2x am 0x%x\n", |
| way, index, valid_chr, va, pa, asid, ca); |
| } |
| |
| void print_itlb_entry (struct way_config_t *way_info, int way, int index) |
| { |
| print_entry (way_info, way, index, |
| read_itlb_virtual (way + (index << way_info->pgsz_log2)), |
| read_itlb_translation (way + (index << way_info->pgsz_log2))); |
| } |
| |
| void print_dtlb_entry (struct way_config_t *way_info, int way, int index) |
| { |
| print_entry (way_info, way, index, |
| read_dtlb_virtual (way + (index << way_info->pgsz_log2)), |
| read_dtlb_translation (way + (index << way_info->pgsz_log2))); |
| } |
| |
| void dump_itlb (void) |
| { |
| int way, index; |
| |
| printk ("\nITLB: ways = %d\n", XCHAL_ITLB_WAYS); |
| |
| for (way = 0; way < XCHAL_ITLB_WAYS; way++) { |
| printk ("\nWay: %d, Entries: %d, MinPageSize: %d, Type: %s\n", |
| way, itlb[way].indicies, |
| itlb[way].pgsz_log2, way_type(itlb[way].arf)); |
| for (index = 0; index < itlb[way].indicies; index++) { |
| print_itlb_entry(&itlb[way], way, index); |
| } |
| } |
| } |
| |
| void dump_dtlb (void) |
| { |
| int way, index; |
| |
| printk ("\nDTLB: ways = %d\n", XCHAL_DTLB_WAYS); |
| |
| for (way = 0; way < XCHAL_DTLB_WAYS; way++) { |
| printk ("\nWay: %d, Entries: %d, MinPageSize: %d, Type: %s\n", |
| way, dtlb[way].indicies, |
| dtlb[way].pgsz_log2, way_type(dtlb[way].arf)); |
| for (index = 0; index < dtlb[way].indicies; index++) { |
| print_dtlb_entry(&dtlb[way], way, index); |
| } |
| } |
| } |
| |
| void dump_tlb (tlb_dump_entry_t *tinfo, struct way_config_t *config, |
| int entries, int ways, int type, int show_invalid) |
| { |
| tlb_dump_entry_t *e = tinfo; |
| int way, i; |
| |
| /* Gather all info: */ |
| for (way = 0; way < ways; way++) { |
| struct way_config_t *cfg = config + way; |
| for (i = 0; i < cfg->indicies; i++) { |
| unsigned wayindex = way + (i << cfg->pgsz_log2); |
| unsigned vv = (type ? read_dtlb_virtual (wayindex) |
| : read_itlb_virtual (wayindex)); |
| unsigned pp = (type ? read_dtlb_translation (wayindex) |
| : read_itlb_translation (wayindex)); |
| |
| /* Compute and incorporate the effect of the index bits on the |
| * va. It's more useful for kernel debugging, since we always |
| * want to know the effective va anyway. */ |
| |
| e->va = (vv & ~((1 << (cfg->pgsz_log2 + cfg->indicies_log2)) - 1)); |
| e->va += (i << cfg->pgsz_log2); |
| e->pa = (pp & ~((1 << cfg->pgsz_log2) - 1)); |
| e->asid = (vv & ((1 << XCHAL_MMU_ASID_BITS) - 1)); |
| e->ca = (pp & ((1 << XCHAL_MMU_CA_BITS) - 1)); |
| e->way = way; |
| e->index = i; |
| e->pgsz_log2 = cfg->pgsz_log2; |
| e->type = type; |
| e++; |
| } |
| } |
| #if 1 |
| /* Sort by ASID and VADDR: */ |
| sort_tlb_dump_info (tinfo, entries); |
| #endif |
| |
| /* Display all sorted info: */ |
| printk ("\n%cTLB dump:\n", (type ? 'D' : 'I')); |
| for (e = tinfo, i = 0; i < entries; i++, e++) { |
| #if 0 |
| if (e->asid == 0 && !show_invalid) |
| continue; |
| #endif |
| printk ("%c way=%d i=%d ASID=%02X V=%08X -> P=%08X CA=%X (%d %cB)\n", |
| (e->type ? 'D' : 'I'), e->way, e->index, |
| e->asid, e->va, e->pa, e->ca, |
| (1 << (e->pgsz_log2 % 10)), |
| " kMG"[e->pgsz_log2 / 10] |
| ); |
| } |
| } |
| |
| void dump_tlbs2 (int showinv) |
| { |
| dump_tlb (itlb_dump_info, itlb, ITLB_TOTAL_ENTRIES, XCHAL_ITLB_WAYS, 0, showinv); |
| dump_tlb (dtlb_dump_info, dtlb, DTLB_TOTAL_ENTRIES, XCHAL_DTLB_WAYS, 1, showinv); |
| } |
| |
| void dump_all_tlbs (void) |
| { |
| dump_tlbs2 (1); |
| } |
| |
| void dump_valid_tlbs (void) |
| { |
| dump_tlbs2 (0); |
| } |
| |
| |
| void dump_tlbs (void) |
| { |
| dump_itlb(); |
| dump_dtlb(); |
| } |
| |
| void dump_cache_tag(int dcache, int idx) |
| { |
| int w, i, s, e; |
| unsigned long tag, index; |
| unsigned long num_lines, num_ways, cache_size, line_size; |
| |
| num_ways = dcache ? XCHAL_DCACHE_WAYS : XCHAL_ICACHE_WAYS; |
| cache_size = dcache ? XCHAL_DCACHE_SIZE : XCHAL_ICACHE_SIZE; |
| line_size = dcache ? XCHAL_DCACHE_LINESIZE : XCHAL_ICACHE_LINESIZE; |
| |
| num_lines = cache_size / num_ways; |
| |
| s = 0; e = num_lines; |
| |
| if (idx >= 0) |
| e = (s = idx * line_size) + 1; |
| |
| for (i = s; i < e; i+= line_size) { |
| printk("\nline %#08x:", i); |
| for (w = 0; w < num_ways; w++) { |
| index = w * num_lines + i; |
| if (dcache) |
| __asm__ __volatile__("ldct %0, %1\n\t" |
| : "=a"(tag) : "a"(index)); |
| else |
| __asm__ __volatile__("lict %0, %1\n\t" |
| : "=a"(tag) : "a"(index)); |
| |
| printk(" %#010lx", tag); |
| } |
| } |
| printk ("\n"); |
| } |
| |
| void dump_icache(int index) |
| { |
| unsigned long data, addr; |
| int w, i; |
| |
| const unsigned long num_ways = XCHAL_ICACHE_WAYS; |
| const unsigned long cache_size = XCHAL_ICACHE_SIZE; |
| const unsigned long line_size = XCHAL_ICACHE_LINESIZE; |
| const unsigned long num_lines = cache_size / num_ways / line_size; |
| |
| for (w = 0; w < num_ways; w++) { |
| printk ("\nWay %d", w); |
| |
| for (i = 0; i < line_size; i+= 4) { |
| addr = w * num_lines + index * line_size + i; |
| __asm__ __volatile__("licw %0, %1\n\t" |
| : "=a"(data) : "a"(addr)); |
| printk(" %#010lx", data); |
| } |
| } |
| printk ("\n"); |
| } |
| |
| void dump_cache_tags(void) |
| { |
| printk("Instruction cache\n"); |
| dump_cache_tag(0, -1); |
| printk("Data cache\n"); |
| dump_cache_tag(1, -1); |
| } |
| |
| #endif |