Path: blob/master/arch/microblaze/kernel/cpu/cache.c
15125 views
/*1* Cache control for MicroBlaze cache memories2*3* Copyright (C) 2007-2009 Michal Simek <[email protected]>4* Copyright (C) 2007-2009 PetaLogix5* Copyright (C) 2007-2009 John Williams <[email protected]>6*7* This file is subject to the terms and conditions of the GNU General8* Public License. See the file COPYING in the main directory of this9* archive for more details.10*/1112#include <asm/cacheflush.h>13#include <linux/cache.h>14#include <asm/cpuinfo.h>15#include <asm/pvr.h>1617static inline void __enable_icache_msr(void)18{19__asm__ __volatile__ (" msrset r0, %0; \20nop; " \21: : "i" (MSR_ICE) : "memory");22}2324static inline void __disable_icache_msr(void)25{26__asm__ __volatile__ (" msrclr r0, %0; \27nop; " \28: : "i" (MSR_ICE) : "memory");29}3031static inline void __enable_dcache_msr(void)32{33__asm__ __volatile__ (" msrset r0, %0; \34nop; " \35: \36: "i" (MSR_DCE) \37: "memory");38}3940static inline void __disable_dcache_msr(void)41{42__asm__ __volatile__ (" msrclr r0, %0; \43nop; " \44: \45: "i" (MSR_DCE) \46: "memory");47}4849static inline void __enable_icache_nomsr(void)50{51__asm__ __volatile__ (" mfs r12, rmsr; \52nop; \53ori r12, r12, %0; \54mts rmsr, r12; \55nop; " \56: \57: "i" (MSR_ICE) \58: "memory", "r12");59}6061static inline void __disable_icache_nomsr(void)62{63__asm__ __volatile__ (" mfs r12, rmsr; \64nop; \65andi r12, r12, ~%0; \66mts rmsr, r12; \67nop; " \68: \69: "i" (MSR_ICE) \70: "memory", "r12");71}7273static inline void __enable_dcache_nomsr(void)74{75__asm__ __volatile__ (" mfs r12, rmsr; \76nop; \77ori r12, r12, %0; \78mts rmsr, r12; \79nop; " \80: \81: "i" (MSR_DCE) \82: "memory", "r12");83}8485static inline void __disable_dcache_nomsr(void)86{87__asm__ __volatile__ (" mfs r12, rmsr; \88nop; \89andi r12, r12, ~%0; \90mts rmsr, r12; \91nop; " \92: \93: "i" (MSR_DCE) \94: "memory", "r12");95}969798/* Helper macro for computing the limits of cache range loops99*100* End address can be unaligned which is OK for C implementation.101* ASM implementation align it in ASM macros102*/103#define CACHE_LOOP_LIMITS(start, end, cache_line_length, cache_size) \104do { \105int align = ~(cache_line_length - 1); \106end = min(start + cache_size, end); \107start &= align; \108} while (0);109110/*111* Helper macro to loop over the specified cache_size/line_length and112* execute 'op' on that cacheline113*/114#define CACHE_ALL_LOOP(cache_size, line_length, op) \115do { \116unsigned int len = cache_size - line_length; \117int step = -line_length; \118WARN_ON(step >= 0); \119\120__asm__ __volatile__ (" 1: " #op " %0, r0; \121bgtid %0, 1b; \122addk %0, %0, %1; \123" : : "r" (len), "r" (step) \124: "memory"); \125} while (0);126127/* Used for wdc.flush/clear which can use rB for offset which is not possible128* to use for simple wdc or wic.129*130* start address is cache aligned131* end address is not aligned, if end is aligned then I have to subtract132* cacheline length because I can't flush/invalidate the next cacheline.133* If is not, I align it because I will flush/invalidate whole line.134*/135#define CACHE_RANGE_LOOP_2(start, end, line_length, op) \136do { \137int step = -line_length; \138int align = ~(line_length - 1); \139int count; \140end = ((end & align) == end) ? end - line_length : end & align; \141count = end - start; \142WARN_ON(count < 0); \143\144__asm__ __volatile__ (" 1: " #op " %0, %1; \145bgtid %1, 1b; \146addk %1, %1, %2; \147" : : "r" (start), "r" (count), \148"r" (step) : "memory"); \149} while (0);150151/* It is used only first parameter for OP - for wic, wdc */152#define CACHE_RANGE_LOOP_1(start, end, line_length, op) \153do { \154int volatile temp; \155int align = ~(line_length - 1); \156end = ((end & align) == end) ? end - line_length : end & align; \157WARN_ON(end - start < 0); \158\159__asm__ __volatile__ (" 1: " #op " %1, r0; \160cmpu %0, %1, %2; \161bgtid %0, 1b; \162addk %1, %1, %3; \163" : : "r" (temp), "r" (start), "r" (end),\164"r" (line_length) : "memory"); \165} while (0);166167#define ASM_LOOP168169static void __flush_icache_range_msr_irq(unsigned long start, unsigned long end)170{171unsigned long flags;172#ifndef ASM_LOOP173int i;174#endif175pr_debug("%s: start 0x%x, end 0x%x\n", __func__,176(unsigned int)start, (unsigned int) end);177178CACHE_LOOP_LIMITS(start, end,179cpuinfo.icache_line_length, cpuinfo.icache_size);180181local_irq_save(flags);182__disable_icache_msr();183184#ifdef ASM_LOOP185CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);186#else187for (i = start; i < end; i += cpuinfo.icache_line_length)188__asm__ __volatile__ ("wic %0, r0;" \189: : "r" (i));190#endif191__enable_icache_msr();192local_irq_restore(flags);193}194195static void __flush_icache_range_nomsr_irq(unsigned long start,196unsigned long end)197{198unsigned long flags;199#ifndef ASM_LOOP200int i;201#endif202pr_debug("%s: start 0x%x, end 0x%x\n", __func__,203(unsigned int)start, (unsigned int) end);204205CACHE_LOOP_LIMITS(start, end,206cpuinfo.icache_line_length, cpuinfo.icache_size);207208local_irq_save(flags);209__disable_icache_nomsr();210211#ifdef ASM_LOOP212CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);213#else214for (i = start; i < end; i += cpuinfo.icache_line_length)215__asm__ __volatile__ ("wic %0, r0;" \216: : "r" (i));217#endif218219__enable_icache_nomsr();220local_irq_restore(flags);221}222223static void __flush_icache_range_noirq(unsigned long start,224unsigned long end)225{226#ifndef ASM_LOOP227int i;228#endif229pr_debug("%s: start 0x%x, end 0x%x\n", __func__,230(unsigned int)start, (unsigned int) end);231232CACHE_LOOP_LIMITS(start, end,233cpuinfo.icache_line_length, cpuinfo.icache_size);234#ifdef ASM_LOOP235CACHE_RANGE_LOOP_1(start, end, cpuinfo.icache_line_length, wic);236#else237for (i = start; i < end; i += cpuinfo.icache_line_length)238__asm__ __volatile__ ("wic %0, r0;" \239: : "r" (i));240#endif241}242243static void __flush_icache_all_msr_irq(void)244{245unsigned long flags;246#ifndef ASM_LOOP247int i;248#endif249pr_debug("%s\n", __func__);250251local_irq_save(flags);252__disable_icache_msr();253#ifdef ASM_LOOP254CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);255#else256for (i = 0; i < cpuinfo.icache_size;257i += cpuinfo.icache_line_length)258__asm__ __volatile__ ("wic %0, r0;" \259: : "r" (i));260#endif261__enable_icache_msr();262local_irq_restore(flags);263}264265static void __flush_icache_all_nomsr_irq(void)266{267unsigned long flags;268#ifndef ASM_LOOP269int i;270#endif271pr_debug("%s\n", __func__);272273local_irq_save(flags);274__disable_icache_nomsr();275#ifdef ASM_LOOP276CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);277#else278for (i = 0; i < cpuinfo.icache_size;279i += cpuinfo.icache_line_length)280__asm__ __volatile__ ("wic %0, r0;" \281: : "r" (i));282#endif283__enable_icache_nomsr();284local_irq_restore(flags);285}286287static void __flush_icache_all_noirq(void)288{289#ifndef ASM_LOOP290int i;291#endif292pr_debug("%s\n", __func__);293#ifdef ASM_LOOP294CACHE_ALL_LOOP(cpuinfo.icache_size, cpuinfo.icache_line_length, wic);295#else296for (i = 0; i < cpuinfo.icache_size;297i += cpuinfo.icache_line_length)298__asm__ __volatile__ ("wic %0, r0;" \299: : "r" (i));300#endif301}302303static void __invalidate_dcache_all_msr_irq(void)304{305unsigned long flags;306#ifndef ASM_LOOP307int i;308#endif309pr_debug("%s\n", __func__);310311local_irq_save(flags);312__disable_dcache_msr();313#ifdef ASM_LOOP314CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);315#else316for (i = 0; i < cpuinfo.dcache_size;317i += cpuinfo.dcache_line_length)318__asm__ __volatile__ ("wdc %0, r0;" \319: : "r" (i));320#endif321__enable_dcache_msr();322local_irq_restore(flags);323}324325static void __invalidate_dcache_all_nomsr_irq(void)326{327unsigned long flags;328#ifndef ASM_LOOP329int i;330#endif331pr_debug("%s\n", __func__);332333local_irq_save(flags);334__disable_dcache_nomsr();335#ifdef ASM_LOOP336CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc);337#else338for (i = 0; i < cpuinfo.dcache_size;339i += cpuinfo.dcache_line_length)340__asm__ __volatile__ ("wdc %0, r0;" \341: : "r" (i));342#endif343__enable_dcache_nomsr();344local_irq_restore(flags);345}346347static void __invalidate_dcache_all_noirq_wt(void)348{349#ifndef ASM_LOOP350int i;351#endif352pr_debug("%s\n", __func__);353#ifdef ASM_LOOP354CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length, wdc)355#else356for (i = 0; i < cpuinfo.dcache_size;357i += cpuinfo.dcache_line_length)358__asm__ __volatile__ ("wdc %0, r0;" \359: : "r" (i));360#endif361}362363/* FIXME It is blindly invalidation as is expected364* but can't be called on noMMU in microblaze_cache_init below365*366* MS: noMMU kernel won't boot if simple wdc is used367* The reason should be that there are discared data which kernel needs368*/369static void __invalidate_dcache_all_wb(void)370{371#ifndef ASM_LOOP372int i;373#endif374pr_debug("%s\n", __func__);375#ifdef ASM_LOOP376CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,377wdc)378#else379for (i = 0; i < cpuinfo.dcache_size;380i += cpuinfo.dcache_line_length)381__asm__ __volatile__ ("wdc %0, r0;" \382: : "r" (i));383#endif384}385386static void __invalidate_dcache_range_wb(unsigned long start,387unsigned long end)388{389#ifndef ASM_LOOP390int i;391#endif392pr_debug("%s: start 0x%x, end 0x%x\n", __func__,393(unsigned int)start, (unsigned int) end);394395CACHE_LOOP_LIMITS(start, end,396cpuinfo.dcache_line_length, cpuinfo.dcache_size);397#ifdef ASM_LOOP398CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.clear);399#else400for (i = start; i < end; i += cpuinfo.dcache_line_length)401__asm__ __volatile__ ("wdc.clear %0, r0;" \402: : "r" (i));403#endif404}405406static void __invalidate_dcache_range_nomsr_wt(unsigned long start,407unsigned long end)408{409#ifndef ASM_LOOP410int i;411#endif412pr_debug("%s: start 0x%x, end 0x%x\n", __func__,413(unsigned int)start, (unsigned int) end);414CACHE_LOOP_LIMITS(start, end,415cpuinfo.dcache_line_length, cpuinfo.dcache_size);416417#ifdef ASM_LOOP418CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);419#else420for (i = start; i < end; i += cpuinfo.dcache_line_length)421__asm__ __volatile__ ("wdc %0, r0;" \422: : "r" (i));423#endif424}425426static void __invalidate_dcache_range_msr_irq_wt(unsigned long start,427unsigned long end)428{429unsigned long flags;430#ifndef ASM_LOOP431int i;432#endif433pr_debug("%s: start 0x%x, end 0x%x\n", __func__,434(unsigned int)start, (unsigned int) end);435CACHE_LOOP_LIMITS(start, end,436cpuinfo.dcache_line_length, cpuinfo.dcache_size);437438local_irq_save(flags);439__disable_dcache_msr();440441#ifdef ASM_LOOP442CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);443#else444for (i = start; i < end; i += cpuinfo.dcache_line_length)445__asm__ __volatile__ ("wdc %0, r0;" \446: : "r" (i));447#endif448449__enable_dcache_msr();450local_irq_restore(flags);451}452453static void __invalidate_dcache_range_nomsr_irq(unsigned long start,454unsigned long end)455{456unsigned long flags;457#ifndef ASM_LOOP458int i;459#endif460pr_debug("%s: start 0x%x, end 0x%x\n", __func__,461(unsigned int)start, (unsigned int) end);462463CACHE_LOOP_LIMITS(start, end,464cpuinfo.dcache_line_length, cpuinfo.dcache_size);465466local_irq_save(flags);467__disable_dcache_nomsr();468469#ifdef ASM_LOOP470CACHE_RANGE_LOOP_1(start, end, cpuinfo.dcache_line_length, wdc);471#else472for (i = start; i < end; i += cpuinfo.dcache_line_length)473__asm__ __volatile__ ("wdc %0, r0;" \474: : "r" (i));475#endif476477__enable_dcache_nomsr();478local_irq_restore(flags);479}480481static void __flush_dcache_all_wb(void)482{483#ifndef ASM_LOOP484int i;485#endif486pr_debug("%s\n", __func__);487#ifdef ASM_LOOP488CACHE_ALL_LOOP(cpuinfo.dcache_size, cpuinfo.dcache_line_length,489wdc.flush);490#else491for (i = 0; i < cpuinfo.dcache_size;492i += cpuinfo.dcache_line_length)493__asm__ __volatile__ ("wdc.flush %0, r0;" \494: : "r" (i));495#endif496}497498static void __flush_dcache_range_wb(unsigned long start, unsigned long end)499{500#ifndef ASM_LOOP501int i;502#endif503pr_debug("%s: start 0x%x, end 0x%x\n", __func__,504(unsigned int)start, (unsigned int) end);505506CACHE_LOOP_LIMITS(start, end,507cpuinfo.dcache_line_length, cpuinfo.dcache_size);508#ifdef ASM_LOOP509CACHE_RANGE_LOOP_2(start, end, cpuinfo.dcache_line_length, wdc.flush);510#else511for (i = start; i < end; i += cpuinfo.dcache_line_length)512__asm__ __volatile__ ("wdc.flush %0, r0;" \513: : "r" (i));514#endif515}516517/* struct for wb caches and for wt caches */518struct scache *mbc;519520/* new wb cache model */521static const struct scache wb_msr = {522.ie = __enable_icache_msr,523.id = __disable_icache_msr,524.ifl = __flush_icache_all_noirq,525.iflr = __flush_icache_range_noirq,526.iin = __flush_icache_all_noirq,527.iinr = __flush_icache_range_noirq,528.de = __enable_dcache_msr,529.dd = __disable_dcache_msr,530.dfl = __flush_dcache_all_wb,531.dflr = __flush_dcache_range_wb,532.din = __invalidate_dcache_all_wb,533.dinr = __invalidate_dcache_range_wb,534};535536/* There is only difference in ie, id, de, dd functions */537static const struct scache wb_nomsr = {538.ie = __enable_icache_nomsr,539.id = __disable_icache_nomsr,540.ifl = __flush_icache_all_noirq,541.iflr = __flush_icache_range_noirq,542.iin = __flush_icache_all_noirq,543.iinr = __flush_icache_range_noirq,544.de = __enable_dcache_nomsr,545.dd = __disable_dcache_nomsr,546.dfl = __flush_dcache_all_wb,547.dflr = __flush_dcache_range_wb,548.din = __invalidate_dcache_all_wb,549.dinr = __invalidate_dcache_range_wb,550};551552/* Old wt cache model with disabling irq and turn off cache */553static const struct scache wt_msr = {554.ie = __enable_icache_msr,555.id = __disable_icache_msr,556.ifl = __flush_icache_all_msr_irq,557.iflr = __flush_icache_range_msr_irq,558.iin = __flush_icache_all_msr_irq,559.iinr = __flush_icache_range_msr_irq,560.de = __enable_dcache_msr,561.dd = __disable_dcache_msr,562.dfl = __invalidate_dcache_all_msr_irq,563.dflr = __invalidate_dcache_range_msr_irq_wt,564.din = __invalidate_dcache_all_msr_irq,565.dinr = __invalidate_dcache_range_msr_irq_wt,566};567568static const struct scache wt_nomsr = {569.ie = __enable_icache_nomsr,570.id = __disable_icache_nomsr,571.ifl = __flush_icache_all_nomsr_irq,572.iflr = __flush_icache_range_nomsr_irq,573.iin = __flush_icache_all_nomsr_irq,574.iinr = __flush_icache_range_nomsr_irq,575.de = __enable_dcache_nomsr,576.dd = __disable_dcache_nomsr,577.dfl = __invalidate_dcache_all_nomsr_irq,578.dflr = __invalidate_dcache_range_nomsr_irq,579.din = __invalidate_dcache_all_nomsr_irq,580.dinr = __invalidate_dcache_range_nomsr_irq,581};582583/* New wt cache model for newer Microblaze versions */584static const struct scache wt_msr_noirq = {585.ie = __enable_icache_msr,586.id = __disable_icache_msr,587.ifl = __flush_icache_all_noirq,588.iflr = __flush_icache_range_noirq,589.iin = __flush_icache_all_noirq,590.iinr = __flush_icache_range_noirq,591.de = __enable_dcache_msr,592.dd = __disable_dcache_msr,593.dfl = __invalidate_dcache_all_noirq_wt,594.dflr = __invalidate_dcache_range_nomsr_wt,595.din = __invalidate_dcache_all_noirq_wt,596.dinr = __invalidate_dcache_range_nomsr_wt,597};598599static const struct scache wt_nomsr_noirq = {600.ie = __enable_icache_nomsr,601.id = __disable_icache_nomsr,602.ifl = __flush_icache_all_noirq,603.iflr = __flush_icache_range_noirq,604.iin = __flush_icache_all_noirq,605.iinr = __flush_icache_range_noirq,606.de = __enable_dcache_nomsr,607.dd = __disable_dcache_nomsr,608.dfl = __invalidate_dcache_all_noirq_wt,609.dflr = __invalidate_dcache_range_nomsr_wt,610.din = __invalidate_dcache_all_noirq_wt,611.dinr = __invalidate_dcache_range_nomsr_wt,612};613614/* CPU version code for 7.20.c - see arch/microblaze/kernel/cpu/cpuinfo.c */615#define CPUVER_7_20_A 0x0c616#define CPUVER_7_20_D 0x0f617618#define INFO(s) printk(KERN_INFO "cache: " s "\n");619620void microblaze_cache_init(void)621{622if (cpuinfo.use_instr & PVR2_USE_MSR_INSTR) {623if (cpuinfo.dcache_wb) {624INFO("wb_msr");625mbc = (struct scache *)&wb_msr;626if (cpuinfo.ver_code <= CPUVER_7_20_D) {627/* MS: problem with signal handling - hw bug */628INFO("WB won't work properly");629}630} else {631if (cpuinfo.ver_code >= CPUVER_7_20_A) {632INFO("wt_msr_noirq");633mbc = (struct scache *)&wt_msr_noirq;634} else {635INFO("wt_msr");636mbc = (struct scache *)&wt_msr;637}638}639} else {640if (cpuinfo.dcache_wb) {641INFO("wb_nomsr");642mbc = (struct scache *)&wb_nomsr;643if (cpuinfo.ver_code <= CPUVER_7_20_D) {644/* MS: problem with signal handling - hw bug */645INFO("WB won't work properly");646}647} else {648if (cpuinfo.ver_code >= CPUVER_7_20_A) {649INFO("wt_nomsr_noirq");650mbc = (struct scache *)&wt_nomsr_noirq;651} else {652INFO("wt_nomsr");653mbc = (struct scache *)&wt_nomsr;654}655}656}657/* FIXME Invalidation is done in U-BOOT658* WT cache: Data is already written to main memory659* WB cache: Discard data on noMMU which caused that kernel doesn't boot660*/661/* invalidate_dcache(); */662enable_dcache();663664invalidate_icache();665enable_icache();666}667668669