blob: a945f9e0ba40f5e1f7204474adf460080fbcc4bc [file] [log] [blame]
/*
* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/msm_rtb.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <asm/cputype.h>
#include <soc/qcom/kryo-l2-accessors.h>
#define L2CPUSRSELR_EL1 S3_3_c15_c0_6
#define L2CPUSRDR_EL1 S3_3_c15_c0_7
#define arm64_sys_reg_read(reg) ({ \
u64 __val; \
asm volatile("mrs %0, " __stringify(reg) : "=r" (__val)); \
__val; \
})
#define arm64_sys_reg_write(reg, val) { \
asm volatile("msr " __stringify(reg) ", %0" : : "r" (val)); \
}
static DEFINE_RAW_SPINLOCK(l2_access_lock);
/**
* set_l2_indirect_reg: write value to an L2 register
* @reg: Address of L2 register.
* @value: Value to be written to register.
*
* Use architecturally required barriers for ordering between system register
* accesses, and system registers with respect to device memory
*/
void set_l2_indirect_reg(u64 reg, u64 val)
{
unsigned long flags;
mb();
raw_spin_lock_irqsave(&l2_access_lock, flags);
uncached_logk(LOGK_L2CPWRITE, (void *)reg);
arm64_sys_reg_write(L2CPUSRSELR_EL1, reg);
isb();
arm64_sys_reg_write(L2CPUSRDR_EL1, val);
isb();
raw_spin_unlock_irqrestore(&l2_access_lock, flags);
}
EXPORT_SYMBOL(set_l2_indirect_reg);
/**
* get_l2_indirect_reg: read an L2 register value
* @reg: Address of L2 register.
*
* Use architecturally required barriers for ordering between system register
* accesses, and system registers with respect to device memory
*/
u64 get_l2_indirect_reg(u64 reg)
{
u64 val;
unsigned long flags;
raw_spin_lock_irqsave(&l2_access_lock, flags);
uncached_logk(LOGK_L2CPREAD, (void *)reg);
arm64_sys_reg_write(L2CPUSRSELR_EL1, reg);
isb();
val = arm64_sys_reg_read(L2CPUSRDR_EL1);
raw_spin_unlock_irqrestore(&l2_access_lock, flags);
return val;
}
EXPORT_SYMBOL(get_l2_indirect_reg);
#if defined(CONFIG_DEBUG_FS)
static u32 debug_addr;
static int debug_target_cpu;
static void remote_l2_ia_read(void *data)
{
u64 *val = data;
*val = get_l2_indirect_reg(debug_addr);
}
static void remote_l2_ia_write(void *data)
{
u64 *val = data;
set_l2_indirect_reg(debug_addr, *val);
}
static int l2_indirect_target_cpu_set(void *data, u64 val)
{
if (val > num_possible_cpus())
return -EINVAL;
debug_target_cpu = val;
return 0;
}
static int l2_indirect_target_cpu_get(void *data, u64 *val)
{
*val = debug_target_cpu;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(l2_ia_cpu_fops, l2_indirect_target_cpu_get,
l2_indirect_target_cpu_set, "%llu\n");
static int l2_indirect_addr_set(void *data, u64 val)
{
debug_addr = val;
return 0;
}
static int l2_indirect_addr_get(void *data, u64 *val)
{
*val = debug_addr;
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(l2_ia_addr_fops, l2_indirect_addr_get,
l2_indirect_addr_set, "%llu\n");
static int l2_indirect_val_set(void *data, u64 val)
{
return smp_call_function_single(debug_target_cpu, remote_l2_ia_write,
&val, 1);
}
DEFINE_SIMPLE_ATTRIBUTE(l2_ia_set_fops, NULL,
l2_indirect_val_set, "%llu\n");
static int l2_indirect_get(void *data, u64 *val)
{
return smp_call_function_single(debug_target_cpu, remote_l2_ia_read,
val, 1);
}
DEFINE_SIMPLE_ATTRIBUTE(l2_ia_get_fops, l2_indirect_get,
NULL, "%llu\n");
/**
* l2_ia_debug_init() - Initialize L2 indirect access register debugfs
*/
static int l2_ia_debug_init(void)
{
static struct dentry *debugfs_base;
debugfs_base = debugfs_create_dir("l2_indirect", NULL);
if (!debugfs_base)
return -ENOMEM;
if (!debugfs_create_file("get", S_IRUGO, debugfs_base, NULL,
&l2_ia_get_fops))
return -ENOMEM;
if (!debugfs_create_file("set", S_IRUGO | S_IWUSR, debugfs_base, NULL,
&l2_ia_set_fops))
return -ENOMEM;
if (!debugfs_create_file("address", S_IRUGO | S_IWUSR, debugfs_base,
NULL, &l2_ia_addr_fops))
return -ENOMEM;
if (!debugfs_create_file("target_cpu", S_IRUGO | S_IWUSR, debugfs_base,
NULL, &l2_ia_cpu_fops))
return -ENOMEM;
return 0;
}
late_initcall(l2_ia_debug_init);
#endif /* CONFIG_DEBUG_FS */