blob: 5d17dbedc58ce736c90da64126e7266a028e95e4 [file] [log] [blame]
/*
* l2cache-control.c
*
* Meta Level 2 cache sysfs interface
*
* Copyright (C) 2011-2012 Imagination Technologies Ltd.
* Written by James Hogan <james.hogan@imgtec.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <asm/core-sysfs.h>
#include <asm/l2cache.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h>
static ssize_t show_l2c_enabled(struct device *sysdev,
struct device_attribute *attr, char *buf)
{
ssize_t err;
int val;
val = !!meta_l2c_is_enabled();
err = sprintf(buf, "%d\n", val);
return err;
}
static ssize_t store_l2c_enabled(struct device *sysdev,
struct device_attribute *attr, const char *buf,
size_t count)
{
unsigned long val;
if (kstrtoul(buf, 10, &val))
return -EINVAL;
if (val) {
pr_info("L2 Cache: Enabling... ");
if (meta_l2c_enable())
pr_cont("already enabled\n");
else
pr_cont("done\n");
} else {
pr_info("L2 Cache: Disabling... ");
if (meta_l2c_disable())
pr_cont("already disabled\n");
else
pr_cont("done\n");
}
return count;
}
static ssize_t show_l2c_prefetch(struct device *sysdev,
struct device_attribute *attr, char *buf)
{
ssize_t err;
int val;
val = !!meta_l2c_pf_is_enabled();
err = sprintf(buf, "%d\n", val);
return err;
}
static ssize_t store_l2c_prefetch(struct device *sysdev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long val;
if (kstrtoul(buf, 10, &val))
return -EINVAL;
if (val) {
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: Disabling prefetch... ");
if (!meta_l2c_pf_enable(0))
pr_cont("already disabled\n");
else
pr_cont("done\n");
}
return count;
}
static ssize_t show_l2c_writeback(struct device *sysdev,
struct device_attribute *attr, char *buf)
{
ssize_t err;
/* when read, we return whether the L2 is a writeback cache */
err = sprintf(buf, "%d\n", !!meta_l2c_is_writeback());
return err;
}
static ssize_t store_l2c_writeback(struct device *sysdev,
struct device_attribute *attr,
const char *buf, size_t count)
{
unsigned long val;
if (kstrtoul(buf, 10, &val))
return -EINVAL;
if (val)
meta_l2c_writeback();
return count;
}
static ssize_t show_l2c_flush(struct device *sysdev,
struct device_attribute *attr, char *buf)
{
ssize_t err;
err = sprintf(buf, "%d\n", 0);
return err;
}
static ssize_t store_l2c_flush(struct device *sysdev,
struct device_attribute *attr, const char *buf,
size_t count)
{
unsigned long val;
if (kstrtoul(buf, 10, &val))
return -EINVAL;
if (val)
meta_l2c_flush();
return count;
}
static ssize_t type_show(struct device *sysdev, struct device_attribute *attr,
char *buf)
{
ssize_t err;
const char *type;
if (meta_l2c_is_unified()) {
type = "Unified";
} else {
/*
* Should be "Instruction" or "Data" really, but we're
* representing the L2 cache as a whole.
*/
type = "Separate";
}
err = sprintf(buf, "%s\n", type);
return err;
}
static ssize_t level_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ssize_t err;
err = sprintf(buf, "%d\n", 2);
return err;
}
static ssize_t size_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ssize_t err;
err = sprintf(buf, "%uK\n", meta_l2c_size() >> 10);
return err;
}
static ssize_t coherency_line_size_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
ssize_t err;
err = sprintf(buf, "%u\n", meta_l2c_linesize());
return err;
}
static ssize_t number_of_sets_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
ssize_t err;
unsigned int sets;
sets = meta_l2c_size() / (meta_l2c_ways() * meta_l2c_linesize());
err = sprintf(buf, "%u\n", sets);
return err;
}
static ssize_t ways_of_associativity_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
ssize_t err;
err = sprintf(buf, "%u\n", meta_l2c_ways());
return err;
}
static ssize_t revision_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ssize_t err;
err = sprintf(buf, "%u\n", meta_l2c_revision());
return err;
}
static ssize_t config_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
ssize_t err;
err = sprintf(buf, "0x%08x\n", meta_l2c_config());
return err;
}
static struct device_attribute l2c_attrs[] = {
/*
* These are fairly standard attributes, used by other architectures in
* /sys/devices/system/cpu/cpuX/cache/indexX/ (but on Meta they're
* elsewhere).
*/
__ATTR_RO(type),
__ATTR_RO(level),
__ATTR_RO(size),
__ATTR_RO(coherency_line_size),
__ATTR_RO(number_of_sets),
__ATTR_RO(ways_of_associativity),
/*
* Other read only attributes, specific to Meta.
*/
__ATTR_RO(revision),
__ATTR_RO(config),
/*
* These can be used to perform operations on the cache, such as
* enabling the cache and prefetch, and triggering a full writeback or
* flush.
*/
__ATTR(enabled, 0644, show_l2c_enabled, store_l2c_enabled),
__ATTR(prefetch, 0644, show_l2c_prefetch, store_l2c_prefetch),
__ATTR(writeback, 0644, show_l2c_writeback, store_l2c_writeback),
__ATTR(flush, 0644, show_l2c_flush, store_l2c_flush),
};
static struct device device_cache_l2 = {
.bus = &cache_subsys,
.init_name = "l2",
};
static int __init meta_l2c_sysfs_init(void)
{
int i, ret;
if (!cache_subsys.name)
return -EINVAL;
/* if there's no L2 cache, don't add the sysfs nodes */
if (!meta_l2c_is_present())
return 0;
ret = device_register(&device_cache_l2);
if (ret)
return ret;
for (i = 0; i < ARRAY_SIZE(l2c_attrs); i++) {
ret = device_create_file(&device_cache_l2,
&l2c_attrs[i]);
if (ret)
return ret;
}
return 0;
}
device_initcall(meta_l2c_sysfs_init);