blob: 9d96286da3d7a787e0ab533619a6115e5c62e070 [file] [log] [blame]
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/static_key.h>
#include <asm/l2cache.h>
#include <asm/metag_isa.h>
/* If non-0, then initialise the L2 cache */
static int l2cache_init = 1;
/* If non-0, then initialise the L2 cache prefetch */
static int l2cache_init_pf = 1;
int l2c_pfenable;
struct static_key l2c_has_invalidate = STATIC_KEY_INIT_FALSE;
static volatile u32 l2c_testdata[16] __initdata __aligned(64);
static int __init parse_l2cache(char *p)
{
char *cp = p;
if (get_option(&cp, &l2cache_init) != 1) {
pr_err("Bad l2cache parameter (%s)\n", p);
return 1;
}
return 0;
}
early_param("l2cache", parse_l2cache);
static int __init parse_l2cache_pf(char *p)
{
char *cp = p;
if (get_option(&cp, &l2cache_init_pf) != 1) {
pr_err("Bad l2cache_pf parameter (%s)\n", p);
return 1;
}
return 0;
}
early_param("l2cache_pf", parse_l2cache_pf);
static int __init meta_l2c_test_invalidate(void)
{
if (meta_l2c_is_enabled() && meta_l2c_is_writeback()) {
/* force 1's into memory */
l2c_testdata[0] = 0x11111111;
__builtin_meta2_cachewd((void *)l2c_testdata,
CACHEW_FLUSH_L1D_L2);
/* dirty the L2 with 2's */
l2c_testdata[0] = 0x22222222;
/* read it back to fill a line in L1 too */
(void)l2c_testdata[0];
/* attempt an invalidation of both caches */
__builtin_meta2_cachewd((void *)l2c_testdata,
CACHEW_INVALIDATE_L1D_L2);
/*
* We should now read back the data from memory if the
* invalidate worked
*/
return l2c_testdata[0] == 0x11111111;
}
return 0;
}
static int __init meta_l2c_setup(void)
{
/*
* If the L2 cache isn't even present, don't do anything, but say so in
* the log.
*/
if (!meta_l2c_is_present()) {
pr_info("L2 Cache: Not present\n");
return 0;
}
/*
* Check whether the line size is recognised.
*/
if (!meta_l2c_linesize()) {
pr_warn_once("L2 Cache: unknown line size id (config=0x%08x)\n",
meta_l2c_config());
}
/*
* Initialise state.
*/
l2c_pfenable = _meta_l2c_pf_is_enabled();
/*
* Enable the L2 cache and print to log whether it was already enabled
* by the bootloader.
*/
if (l2cache_init) {
pr_info("L2 Cache: Enabling... ");
if (meta_l2c_enable())
pr_cont("already enabled\n");
else
pr_cont("done\n");
} else {
pr_info("L2 Cache: Not enabling\n");
}
/*
* Enable L2 cache prefetch.
*/
if (l2cache_init_pf) {
pr_info("L2 Cache: Enabling prefetch... ");
if (meta_l2c_pf_enable(1))
pr_cont("already enabled\n");
else
pr_cont("done\n");
} else {
pr_info("L2 Cache: Not enabling prefetch\n");
}
/*
* Test whether invalidate (CACHEWD 0xC) is usable.
*/
if (meta_l2c_test_invalidate())
static_key_slow_inc(&l2c_has_invalidate);
if (!meta_l2c_has_invalidate())
pr_info("L2 Cache: Invalidation op disabled\n");
return 0;
}
core_initcall(meta_l2c_setup);
int meta_l2c_disable(void)
{
unsigned long flags;
int en;
if (!meta_l2c_is_present())
return 1;
/*
* Prevent other threads writing during the writeback, otherwise the
* writes will get "lost" when the L2 is disabled.
*/
__global_lock2(flags);
en = meta_l2c_is_enabled();
if (likely(en)) {
_meta_l2c_pf_enable(0);
wr_fence();
_meta_l2c_purge();
_meta_l2c_enable(0);
}
__global_unlock2(flags);
return !en;
}
int meta_l2c_enable(void)
{
unsigned long flags;
int en;
if (!meta_l2c_is_present())
return 0;
/*
* Init (clearing the L2) can happen while the L2 is disabled, so other
* threads are safe to continue executing, however we must not init the
* cache if it's already enabled (dirty lines would be discarded), so
* this operation should still be atomic with other threads.
*/
__global_lock1(flags);
en = meta_l2c_is_enabled();
if (likely(!en)) {
_meta_l2c_init();
_meta_l2c_enable(1);
_meta_l2c_pf_enable(l2c_pfenable);
}
__global_unlock1(flags);
return en;
}
int meta_l2c_pf_enable(int pfenable)
{
unsigned long flags;
int en = l2c_pfenable;
if (!meta_l2c_is_present())
return 0;
/*
* We read modify write the enable register, so this operation must be
* atomic with other threads.
*/
__global_lock1(flags);
en = l2c_pfenable;
l2c_pfenable = pfenable;
if (meta_l2c_is_enabled())
_meta_l2c_pf_enable(pfenable);
__global_unlock1(flags);
return en;
}
int meta_l2c_flush(void)
{
unsigned long flags;
int en;
/*
* Prevent other threads writing during the writeback. This also
* involves read modify writes.
*/
__global_lock2(flags);
en = meta_l2c_is_enabled();
if (likely(en)) {
_meta_l2c_pf_enable(0);
wr_fence();
_meta_l2c_purge();
_meta_l2c_enable(0);
_meta_l2c_init();
_meta_l2c_enable(1);
_meta_l2c_pf_enable(l2c_pfenable);
}
__global_unlock2(flags);
return !en;
}