mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 17:25:35 +03:00
t23x: ari: build mce ari driver as a module
Bug 3680143 Change-Id: I1092b85239222b6af9a9ecfc536801f298706e60 Signed-off-by: Sanjay Chandrashekara <sanjayc@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2774586 Reviewed-by: Bitan Biswas <bbiswas@nvidia.com> GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
This commit is contained in:
committed by
mobile promotions
parent
460fb0fd97
commit
d61f30d568
@@ -1,5 +1,5 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
|
# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||||
|
|
||||||
obj-m += bad.o
|
obj-m += bad.o
|
||||||
|
|
||||||
@@ -18,3 +18,4 @@ obj-m += tegra-fsicom.o
|
|||||||
|
|
||||||
obj-m += cvnas/
|
obj-m += cvnas/
|
||||||
obj-m += hwpm/
|
obj-m += hwpm/
|
||||||
|
obj-m += mce/
|
||||||
|
|||||||
7
drivers/platform/tegra/mce/Makefile
Normal file
7
drivers/platform/tegra/mce/Makefile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
# Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||||
|
#
|
||||||
|
GCOV_PROFILE := y
|
||||||
|
ccflags-y += -Werror
|
||||||
|
|
||||||
|
obj-m += tegra23x-mce.o tegra-mce.o
|
||||||
461
drivers/platform/tegra/mce/tegra-mce.c
Normal file
461
drivers/platform/tegra/mce/tegra-mce.c
Normal file
@@ -0,0 +1,461 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
// Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||||
|
|
||||||
|
#include <linux/debugfs.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform/tegra/tegra-mce.h>
|
||||||
|
#include <asm/smp_plat.h>
|
||||||
|
|
||||||
|
static struct tegra_mce_ops *mce_ops;
|
||||||
|
|
||||||
|
void tegra_mce_set_ops(struct tegra_mce_ops *tegra_mce_plat_ops)
|
||||||
|
{
|
||||||
|
mce_ops = tegra_mce_plat_ops;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_set_ops);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify power state and wake time for entering upon STANDBYWFI
|
||||||
|
*
|
||||||
|
* @state: requested core power state
|
||||||
|
* @wake_time: wake time in TSC ticks
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_enter_cstate(u32 state, u32 wake_time)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->enter_cstate)
|
||||||
|
return mce_ops->enter_cstate(state, wake_time);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_enter_cstate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify deepest cluster/ccplex/system states allowed.
|
||||||
|
*
|
||||||
|
* @cluster: deepest cluster-wide state
|
||||||
|
* @ccplex: deepest ccplex-wide state
|
||||||
|
* @system: deepest system-wide state
|
||||||
|
* @force: forced system state
|
||||||
|
* @wake_mask: wake mask to be updated
|
||||||
|
* @valid: is wake_mask applicable?
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_update_cstate_info(u32 cluster, u32 ccplex, u32 system,
|
||||||
|
u8 force, u32 wake_mask, bool valid)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->update_cstate_info)
|
||||||
|
return mce_ops->update_cstate_info(cluster, ccplex, system,
|
||||||
|
force, wake_mask, valid);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_update_cstate_info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update threshold for one specific c-state crossover
|
||||||
|
*
|
||||||
|
* @type: type of state crossover.
|
||||||
|
* @time: idle time threshold.
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_update_crossover_time(u32 type, u32 time)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->update_crossover_time)
|
||||||
|
return mce_ops->update_crossover_time(type, time);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_update_crossover_time);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query the runtime stats of a specific cstate
|
||||||
|
*
|
||||||
|
* @state: c-state of the stats.
|
||||||
|
* @stats: output integer to hold the stats.
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_read_cstate_stats(u32 state, u64 *stats)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->read_cstate_stats)
|
||||||
|
return mce_ops->read_cstate_stats(state, stats);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_read_cstate_stats);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrite the runtime stats of a specific c-state
|
||||||
|
*
|
||||||
|
* @state: c-state of the stats.
|
||||||
|
* @stats: integer represents the new stats.
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_write_cstate_stats(u32 state, u32 stats)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->write_cstate_stats)
|
||||||
|
return mce_ops->write_cstate_stats(state, stats);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_write_cstate_stats);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query MCE to determine if SC7 is allowed
|
||||||
|
* given a target core's C-state and wake time
|
||||||
|
*
|
||||||
|
* @state: c-state of the stats.
|
||||||
|
* @stats: integer represents the new stats.
|
||||||
|
* @allowed: pointer to result
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_is_sc7_allowed(u32 state, u32 wake, u32 *allowed)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->is_sc7_allowed)
|
||||||
|
return mce_ops->is_sc7_allowed(state, wake, allowed);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_is_sc7_allowed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bring another offlined core back online to C0 state.
|
||||||
|
*
|
||||||
|
* @cpu: logical cpuid from smp_processor_id()
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_online_core(int cpu)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->online_core)
|
||||||
|
return mce_ops->online_core(cpu);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_online_core);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Program Auto-CC3 feature.
|
||||||
|
*
|
||||||
|
* @ndiv: ndiv of IDLE voltage/freq register
|
||||||
|
* @vindex: vindex of IDLE voltage/freq register
|
||||||
|
* (Not used on tegra19x)
|
||||||
|
* @enable: enable bit for Auto-CC3
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_cc3_ctrl(u32 ndiv, u32 vindex, u8 enable)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->cc3_ctrl)
|
||||||
|
return mce_ops->cc3_ctrl(ndiv, vindex, enable);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_cc3_ctrl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send data to MCE which echoes it back.
|
||||||
|
*
|
||||||
|
* @data: data to be sent to MCE.
|
||||||
|
* @out: output data to hold the response.
|
||||||
|
* @matched: pointer to matching result
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_echo_data(u64 data, u64 *matched)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->echo_data)
|
||||||
|
return mce_ops->echo_data(data, matched);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_echo_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read out MCE API major/minor versions
|
||||||
|
*
|
||||||
|
* @major: output for major number.
|
||||||
|
* @minor: output for minor number.
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_read_versions(u32 *major, u32 *minor)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->read_versions)
|
||||||
|
return mce_ops->read_versions(major, minor);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_read_versions);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read out RT Safe Mask
|
||||||
|
*
|
||||||
|
* @rt_safe_mask: output for rt safe mask.
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_read_rt_safe_mask(u64 *rt_safe_mask)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->read_rt_safe_mask)
|
||||||
|
return mce_ops->read_rt_safe_mask(rt_safe_mask);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_read_rt_safe_mask);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write RT Safe Mask
|
||||||
|
*
|
||||||
|
* @rt_safe_mask: rt safe mask value to be written
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_write_rt_safe_mask(u64 rt_safe_mask)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->write_rt_safe_mask)
|
||||||
|
return mce_ops->write_rt_safe_mask(rt_safe_mask);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_write_rt_safe_mask);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read out RT Window US
|
||||||
|
*
|
||||||
|
* @rt_window_us: output for rt window us
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_read_rt_window_us(u64 *rt_window_us)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->read_rt_window_us)
|
||||||
|
return mce_ops->read_rt_window_us(rt_window_us);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_read_rt_window_us);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write RT Window US
|
||||||
|
*
|
||||||
|
* @rt_window_us: rt window us value to be written
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_write_rt_window_us(u64 rt_window_us)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->write_rt_window_us)
|
||||||
|
return mce_ops->write_rt_window_us(rt_window_us);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_write_rt_window_us);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read out RT Fwd Progress US
|
||||||
|
*
|
||||||
|
* @rt_fwd_progress_us: output for rt fwd progress us
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_read_rt_fwd_progress_us(u64 *rt_fwd_progress_us)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->read_rt_fwd_progress_us)
|
||||||
|
return mce_ops->read_rt_fwd_progress_us(rt_fwd_progress_us);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_read_rt_fwd_progress_us);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write RT Fwd Progress US
|
||||||
|
*
|
||||||
|
* @rt_fwd_progress_us: rt fwd progress us value to be written
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_write_rt_fwd_progress_us(u64 rt_fwd_progress_us)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->write_rt_fwd_progress_us)
|
||||||
|
return mce_ops->write_rt_fwd_progress_us(rt_fwd_progress_us);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_write_rt_fwd_progress_us);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumerate MCE API features
|
||||||
|
*
|
||||||
|
* @features: output feature vector (4bits each)
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_enum_features(u64 *features)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->enum_features)
|
||||||
|
return mce_ops->enum_features(features);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_enum_features);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read uncore MCA errors.
|
||||||
|
*
|
||||||
|
* @cmd: MCA command
|
||||||
|
* @data: output data for the command
|
||||||
|
* @error: error from MCA
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_read_uncore_mca(mca_cmd_t cmd, u64 *data, u32 *error)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->read_uncore_mca)
|
||||||
|
return mce_ops->read_uncore_mca(cmd, data, error);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_read_uncore_mca);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write uncore MCA errors.
|
||||||
|
*
|
||||||
|
* @cmd: MCA command
|
||||||
|
* @data: input data for the command
|
||||||
|
* @error: error from MCA
|
||||||
|
*
|
||||||
|
* Returns 0 if success.
|
||||||
|
*/
|
||||||
|
int tegra_mce_write_uncore_mca(mca_cmd_t cmd, u64 data, u32 *error)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->write_uncore_mca)
|
||||||
|
return mce_ops->write_uncore_mca(cmd, data, error);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_write_uncore_mca);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Query PMU for uncore perfmon counter
|
||||||
|
*
|
||||||
|
* @req input command and counter index
|
||||||
|
* @data output counter value
|
||||||
|
*
|
||||||
|
* Returns status of read request.
|
||||||
|
*/
|
||||||
|
int tegra_mce_read_uncore_perfmon(u32 req, u32 *data)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->read_uncore_perfmon)
|
||||||
|
return mce_ops->read_uncore_perfmon(req, data);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_read_uncore_perfmon);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write PMU reg for uncore perfmon counter
|
||||||
|
*
|
||||||
|
* @req input command and counter index
|
||||||
|
* @data data to be written
|
||||||
|
*
|
||||||
|
* Returns status of write request.
|
||||||
|
*/
|
||||||
|
int tegra_mce_write_uncore_perfmon(u32 req, u32 data)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->write_uncore_perfmon)
|
||||||
|
return mce_ops->write_uncore_perfmon(req, data);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_write_uncore_perfmon);
|
||||||
|
|
||||||
|
int tegra_mce_enable_latic(void)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->enable_latic)
|
||||||
|
return mce_ops->enable_latic();
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_enable_latic);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write to NVG DDA registers
|
||||||
|
*
|
||||||
|
* @index: NVG communication channel id
|
||||||
|
* @value: Register value to be written
|
||||||
|
*
|
||||||
|
* Returns 0 on success
|
||||||
|
*/
|
||||||
|
int tegra_mce_write_dda_ctrl(u32 index, u64 value)
|
||||||
|
{
|
||||||
|
if(mce_ops && mce_ops->write_dda_ctrl)
|
||||||
|
return mce_ops->write_dda_ctrl(index, value);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_write_dda_ctrl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read NVG DDA registers
|
||||||
|
*
|
||||||
|
* @index: NVG communication channel id
|
||||||
|
* @value: Associated register value read
|
||||||
|
*
|
||||||
|
* Returns 0 on success
|
||||||
|
*/
|
||||||
|
int tegra_mce_read_dda_ctrl(u32 index, u64 *value)
|
||||||
|
{
|
||||||
|
if(mce_ops && mce_ops->read_dda_ctrl)
|
||||||
|
return mce_ops->read_dda_ctrl(index, value);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_read_dda_ctrl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read NVG L3 cache control register
|
||||||
|
*
|
||||||
|
* @value: Fill L3 cache ways
|
||||||
|
*
|
||||||
|
* Returns 0 on success
|
||||||
|
*/
|
||||||
|
int tegra_mce_read_l3_cache_ways(u64 *value)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->read_l3_cache_ways)
|
||||||
|
return mce_ops->read_l3_cache_ways(value);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_read_l3_cache_ways);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write L3 cache ways and read back the l3 cache ways written
|
||||||
|
*
|
||||||
|
* @data: L3 cache ways to be writtein
|
||||||
|
* @value: L3 cache ways returrned back
|
||||||
|
*
|
||||||
|
* Returns 0 on success
|
||||||
|
*/
|
||||||
|
int tegra_mce_write_l3_cache_ways(u64 data, u64 *value)
|
||||||
|
{
|
||||||
|
if (mce_ops && mce_ops->write_l3_cache_ways)
|
||||||
|
return mce_ops->write_l3_cache_ways(data, value);
|
||||||
|
else
|
||||||
|
return -ENOTSUPP;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(tegra_mce_write_l3_cache_ways);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("NVIDIA Corporation");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
578
drivers/platform/tegra/mce/tegra23x-mce.c
Normal file
578
drivers/platform/tegra/mce/tegra23x-mce.c
Normal file
@@ -0,0 +1,578 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
// Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||||
|
//
|
||||||
|
#include <linux/cpumask.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/debugfs.h>
|
||||||
|
#include <asm/cputype.h>
|
||||||
|
#include <asm/smp_plat.h>
|
||||||
|
#include <asm/smp.h>
|
||||||
|
#include <linux/platform/tegra/tegra-mce.h>
|
||||||
|
#include <linux/platform/tegra/t23x_ari.h>
|
||||||
|
|
||||||
|
#define MAX_CPUS 12U
|
||||||
|
#define MAX_CORES_PER_CLUSTER 4U
|
||||||
|
#define ARI_TIMEOUT_MAX 2000U /* msec */
|
||||||
|
|
||||||
|
/* Register offsets for ARI request/results*/
|
||||||
|
#define ARI_REQUEST 0x0U
|
||||||
|
#define ARI_REQUEST_EVENT_MASK 0x8U
|
||||||
|
#define ARI_STATUS 0x10U
|
||||||
|
#define ARI_REQUEST_DATA_LO 0x18U
|
||||||
|
#define ARI_REQUEST_DATA_HI 0x20U
|
||||||
|
#define ARI_RESPONSE_DATA_LO 0x28U
|
||||||
|
#define ARI_RESPONSE_DATA_HI 0x30U
|
||||||
|
|
||||||
|
/* Status values for the current request */
|
||||||
|
#define ARI_REQ_PENDING 1U
|
||||||
|
#define ARI_REQ_ONGOING 3U
|
||||||
|
#define ARI_REQUEST_VALID_BIT (1U << 8U)
|
||||||
|
#define ARI_REQUEST_NS_BIT (1U << 31U)
|
||||||
|
|
||||||
|
/* Write Enable bit */
|
||||||
|
#define CACHE_WAYS_WRITE_EN_BIT (1U << 15U)
|
||||||
|
|
||||||
|
static void __iomem *ari_bar_array[MAX_CPUS];
|
||||||
|
|
||||||
|
static inline void ari_mmio_write_32(void __iomem *ari_base,
|
||||||
|
u32 val, u32 reg)
|
||||||
|
{
|
||||||
|
writel(val, ari_base + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 ari_mmio_read_32(void __iomem *ari_base, u32 reg)
|
||||||
|
{
|
||||||
|
return readl(ari_base + reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 ari_get_response_low(void __iomem *ari_base)
|
||||||
|
{
|
||||||
|
return ari_mmio_read_32(ari_base, ARI_RESPONSE_DATA_LO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 ari_get_response_high(void __iomem *ari_base)
|
||||||
|
{
|
||||||
|
return ari_mmio_read_32(ari_base, ARI_RESPONSE_DATA_HI);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ari_clobber_response(void __iomem *ari_base)
|
||||||
|
{
|
||||||
|
ari_mmio_write_32(ari_base, 0, ARI_RESPONSE_DATA_LO);
|
||||||
|
ari_mmio_write_32(ari_base, 0, ARI_RESPONSE_DATA_HI);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t ari_send_request(void __iomem *ari_base, u32 evt_mask,
|
||||||
|
u32 req, u32 lo, u32 hi)
|
||||||
|
{
|
||||||
|
uint32_t timeout = ARI_TIMEOUT_MAX;
|
||||||
|
uint32_t status;
|
||||||
|
int32_t ret = 0;
|
||||||
|
|
||||||
|
/* clobber response */
|
||||||
|
ari_mmio_write_32(ari_base, 0, ARI_RESPONSE_DATA_LO);
|
||||||
|
ari_mmio_write_32(ari_base, 0, ARI_RESPONSE_DATA_HI);
|
||||||
|
|
||||||
|
/* send request */
|
||||||
|
ari_mmio_write_32(ari_base, lo, ARI_REQUEST_DATA_LO);
|
||||||
|
ari_mmio_write_32(ari_base, hi, ARI_REQUEST_DATA_HI);
|
||||||
|
ari_mmio_write_32(ari_base, evt_mask, ARI_REQUEST_EVENT_MASK);
|
||||||
|
ari_mmio_write_32(ari_base,
|
||||||
|
req | ARI_REQUEST_VALID_BIT | ARI_REQUEST_NS_BIT, ARI_REQUEST);
|
||||||
|
|
||||||
|
while (timeout) {
|
||||||
|
status = ari_mmio_read_32(ari_base, ARI_STATUS);
|
||||||
|
if (!(status & (ARI_REQ_ONGOING | ARI_REQ_PENDING)))
|
||||||
|
break;
|
||||||
|
mdelay(1);
|
||||||
|
timeout--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!timeout)
|
||||||
|
ret = -ETIMEDOUT;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t get_ari_address_index(void)
|
||||||
|
{
|
||||||
|
uint64_t mpidr;
|
||||||
|
uint32_t core_id, cluster_id;
|
||||||
|
|
||||||
|
mpidr = read_cpuid_mpidr();
|
||||||
|
cluster_id = MPIDR_AFFINITY_LEVEL(mpidr, 2);
|
||||||
|
core_id = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||||
|
|
||||||
|
return ((cluster_id * MAX_CORES_PER_CLUSTER) + core_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra23x_mce_read_versions(u32 *major, u32 *minor)
|
||||||
|
{
|
||||||
|
uint32_t cpu_idx;
|
||||||
|
int32_t ret = 0;
|
||||||
|
|
||||||
|
if (IS_ERR_OR_NULL(major) || IS_ERR_OR_NULL(minor))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
preempt_disable();
|
||||||
|
cpu_idx = get_ari_address_index();
|
||||||
|
ret = ari_send_request(ari_bar_array[cpu_idx], 0U,
|
||||||
|
(u32)TEGRA_ARI_VERSION, 0U, 0U);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
*major = ari_get_response_low(ari_bar_array[cpu_idx]);
|
||||||
|
*minor = ari_get_response_high(ari_bar_array[cpu_idx]);
|
||||||
|
preempt_enable();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* echo copies data from req_low to resp_low and
|
||||||
|
* data from req_high to resp_high.
|
||||||
|
*/
|
||||||
|
static int tegra23x_mce_echo_data(u64 data, u64 *matched)
|
||||||
|
{
|
||||||
|
uint32_t cpu_idx;
|
||||||
|
u32 input1 = (u32)(data & 0xFFFFFFFF);
|
||||||
|
u32 input2 = (u32)(data >> 32);
|
||||||
|
u64 out1, out2;
|
||||||
|
int32_t ret = 0;
|
||||||
|
|
||||||
|
if (IS_ERR_OR_NULL(matched))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
preempt_disable();
|
||||||
|
cpu_idx = get_ari_address_index();
|
||||||
|
ret = ari_send_request(ari_bar_array[cpu_idx], 0U,
|
||||||
|
(u32)TEGRA_ARI_ECHO, input1, input2);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
out1 = (u64)ari_get_response_low(ari_bar_array[cpu_idx]);
|
||||||
|
out2 = (u64)ari_get_response_high(ari_bar_array[cpu_idx]);
|
||||||
|
*matched = ((out2 << 32) | out1);
|
||||||
|
preempt_enable();
|
||||||
|
|
||||||
|
if (data == *matched)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -ENOMSG;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra23x_mce_read_l4_cache_ways(u64 *value)
|
||||||
|
{
|
||||||
|
uint32_t cpu_idx;
|
||||||
|
u64 out;
|
||||||
|
int32_t ret = 0;
|
||||||
|
|
||||||
|
preempt_disable();
|
||||||
|
cpu_idx = get_ari_address_index();
|
||||||
|
ret = ari_send_request(ari_bar_array[cpu_idx], 0U,
|
||||||
|
(u32)TEGRA_ARI_CCPLEX_CACHE_CONTROL, 0U, 0U);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
out = (u64)ari_get_response_low(ari_bar_array[cpu_idx]);
|
||||||
|
|
||||||
|
*value = out;
|
||||||
|
preempt_enable();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra23x_mce_write_l4_cache_ways(u64 data, u64 *value)
|
||||||
|
{
|
||||||
|
uint32_t cpu_idx;
|
||||||
|
u32 input = (u32)(data & 0x00001F1F);
|
||||||
|
u64 out;
|
||||||
|
int32_t ret = 0;
|
||||||
|
|
||||||
|
if (IS_ERR_OR_NULL(value))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
preempt_disable();
|
||||||
|
cpu_idx = get_ari_address_index();
|
||||||
|
input |= CACHE_WAYS_WRITE_EN_BIT;
|
||||||
|
ret = ari_send_request(ari_bar_array[cpu_idx], 0U,
|
||||||
|
(u32)TEGRA_ARI_CCPLEX_CACHE_CONTROL, input, 0U);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
out = (u64)ari_get_response_low(ari_bar_array[cpu_idx]);
|
||||||
|
*value = out;
|
||||||
|
preempt_enable();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra23x_mce_read_uncore_perfmon(u32 req, u32 *data)
|
||||||
|
{
|
||||||
|
uint32_t cpu_idx;
|
||||||
|
u32 out_lo, out_hi;
|
||||||
|
int32_t ret = 0;
|
||||||
|
|
||||||
|
if (IS_ERR_OR_NULL(data))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
preempt_disable();
|
||||||
|
|
||||||
|
cpu_idx = get_ari_address_index();
|
||||||
|
ret = ari_send_request(ari_bar_array[cpu_idx], 0U,
|
||||||
|
(u32)TEGRA_ARI_PERFMON, req, 0U);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
out_lo = ari_get_response_low(ari_bar_array[cpu_idx]);
|
||||||
|
out_hi = ari_get_response_high(ari_bar_array[cpu_idx]);
|
||||||
|
|
||||||
|
pr_debug("%s: read status = %u\n", __func__, out_lo);
|
||||||
|
|
||||||
|
if (out_lo != 0)
|
||||||
|
return -out_lo;
|
||||||
|
|
||||||
|
*data = out_hi;
|
||||||
|
|
||||||
|
preempt_enable();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra23x_mce_write_uncore_perfmon(u32 req, u32 data)
|
||||||
|
{
|
||||||
|
uint32_t cpu_idx;
|
||||||
|
u32 out_lo, out_hi;
|
||||||
|
int32_t ret = 0;
|
||||||
|
|
||||||
|
preempt_disable();
|
||||||
|
|
||||||
|
cpu_idx = get_ari_address_index();
|
||||||
|
ret = ari_send_request(ari_bar_array[cpu_idx], 0U,
|
||||||
|
(u32)TEGRA_ARI_PERFMON, req, data);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
out_lo = ari_get_response_low(ari_bar_array[cpu_idx]);
|
||||||
|
out_hi = ari_get_response_high(ari_bar_array[cpu_idx]);
|
||||||
|
|
||||||
|
pr_debug("%s: write status = %u\n", __func__, out_lo);
|
||||||
|
|
||||||
|
if (out_lo != 0)
|
||||||
|
return -out_lo;
|
||||||
|
|
||||||
|
preempt_enable();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra23x_mce_read_cstate_stats(u32 state, u64 *stats)
|
||||||
|
{
|
||||||
|
uint32_t cpu_idx;
|
||||||
|
int32_t ret = 0;
|
||||||
|
|
||||||
|
if (IS_ERR_OR_NULL(stats))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
preempt_disable();
|
||||||
|
cpu_idx = get_ari_address_index();
|
||||||
|
ret = ari_send_request(ari_bar_array[cpu_idx], 0U,
|
||||||
|
(u32)TEGRA_ARI_CSTATE_STAT_QUERY, state, 0U);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
*stats = ari_get_response_low(ari_bar_array[cpu_idx]);
|
||||||
|
preempt_enable();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_DEBUG_FS
|
||||||
|
|
||||||
|
static struct dentry *mce_debugfs;
|
||||||
|
|
||||||
|
static int tegra23x_mce_versions_get(void *data, u64 *val)
|
||||||
|
{
|
||||||
|
u32 major = 0;
|
||||||
|
u32 minor = 0;
|
||||||
|
u64 version = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
*val = 0;
|
||||||
|
ret = tegra_mce_read_versions(&major, &minor);
|
||||||
|
if (!ret) {
|
||||||
|
version = (u64)major;
|
||||||
|
*val = (version << 32) | minor;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra23x_mce_echo_set(void *data, u64 val)
|
||||||
|
{
|
||||||
|
u64 matched = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = tegra_mce_echo_data(val, &matched);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tegra_get_cpu_mpidr(void *mpidr)
|
||||||
|
{
|
||||||
|
*((u64 *)mpidr) = read_cpuid_mpidr() & MPIDR_HWID_BITMASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MCE_STAT_ID_SHIFT 16UL
|
||||||
|
#define MAX_CSTATE_ENTRIES 3U
|
||||||
|
#define MAX_CLUSTERS 3U
|
||||||
|
|
||||||
|
struct cstats_req {
|
||||||
|
char *name;
|
||||||
|
uint32_t id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cstats_resp {
|
||||||
|
uint32_t stats[3]; /* entries, entry_time_sum, exit_time_sum */
|
||||||
|
uint32_t log_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct cstats_req core_req[MAX_CSTATE_ENTRIES] = {
|
||||||
|
{ "C7_ENTRIES", TEGRA_ARI_STAT_QUERY_C7_ENTRIES},
|
||||||
|
{ "C7_ENTRY_TIME_SUM", TEGRA_ARI_STAT_QUERY_C7_ENTRY_TIME_SUM},
|
||||||
|
{ "C7_EXIT_TIME_SUM", TEGRA_ARI_STAT_QUERY_C7_EXIT_TIME_SUM},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct cstats_req cluster_req[MAX_CSTATE_ENTRIES] = {
|
||||||
|
{ "CC7_ENTRIES", TEGRA_ARI_STAT_QUERY_CC7_ENTRIES},
|
||||||
|
{ "CC7_ENTRY_TIME_SUM", TEGRA_ARI_STAT_QUERY_CC7_ENTRY_TIME_SUM},
|
||||||
|
{ "CC7_EXIT_TIME_SUM", TEGRA_ARI_STAT_QUERY_CC7_EXIT_TIME_SUM},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct cstats_req system_req[MAX_CSTATE_ENTRIES] = {
|
||||||
|
{ "SC7_ENTRIES", TEGRA_ARI_STAT_QUERY_SC7_ENTRIES},
|
||||||
|
{ "SC7_CCPLEX_ENTRY_TIME_SUM", TEGRA_ARI_STAT_QUERY_SC7_ENTRY_TIME_SUM},
|
||||||
|
{ "SC7_CCPLEX_EXIT_TIME_SUM", TEGRA_ARI_STAT_QUERY_SC7_EXIT_TIME_SUM},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int tegra23x_mce_dbg_cstats_show(struct seq_file *s, void *data)
|
||||||
|
{
|
||||||
|
u64 val;
|
||||||
|
u32 mce_index;
|
||||||
|
uint32_t cpu, mpidr_core, mpidr_cl, mpidr_lin, i, j;
|
||||||
|
struct cstats_resp core_resp[MAX_CPUS] = { 0 };
|
||||||
|
struct cstats_resp cl_resp[MAX_CLUSTERS] = { 0 };
|
||||||
|
struct cstats_resp sys_resp = { 0 };
|
||||||
|
u64 mpidr = 0;
|
||||||
|
|
||||||
|
for_each_online_cpu(cpu) {
|
||||||
|
smp_call_function_single(cpu, tegra_get_cpu_mpidr, &mpidr, true);
|
||||||
|
mpidr_cl = MPIDR_AFFINITY_LEVEL(mpidr, 2);
|
||||||
|
mpidr_core = MPIDR_AFFINITY_LEVEL(mpidr, 1);
|
||||||
|
mpidr_lin = ((mpidr_cl * MAX_CORES_PER_CLUSTER) + mpidr_core);
|
||||||
|
|
||||||
|
/* core cstats */
|
||||||
|
for (i = 0; i < MAX_CSTATE_ENTRIES; i++) {
|
||||||
|
mce_index = (core_req[i].id <<
|
||||||
|
MCE_STAT_ID_SHIFT) + mpidr_lin;
|
||||||
|
if (tegra23x_mce_read_cstate_stats(mce_index, &val))
|
||||||
|
pr_err("mce: failed to read cstat: %x\n", mce_index);
|
||||||
|
else {
|
||||||
|
core_resp[mpidr_lin].stats[i] = val;
|
||||||
|
core_resp[mpidr_lin].log_id = cpu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cluster cstats
|
||||||
|
* for multiple cores in the same cluster we end up calling
|
||||||
|
* more than once. Optimize this later
|
||||||
|
*/
|
||||||
|
for (i = 0; i < MAX_CSTATE_ENTRIES; i++) {
|
||||||
|
mce_index = (cluster_req[i].id <<
|
||||||
|
MCE_STAT_ID_SHIFT) + mpidr_cl;
|
||||||
|
if (tegra23x_mce_read_cstate_stats(mce_index, &val))
|
||||||
|
pr_err("mce: failed to read cstat: %x\n", mce_index);
|
||||||
|
else
|
||||||
|
cl_resp[mpidr_cl].stats[i] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* system cstats */
|
||||||
|
for (i = 0; i < MAX_CSTATE_ENTRIES; i++) {
|
||||||
|
mce_index = (system_req[i].id << MCE_STAT_ID_SHIFT);
|
||||||
|
if (tegra23x_mce_read_cstate_stats(mce_index, &val))
|
||||||
|
pr_err("mce: failed to read cstat: %x\n", mce_index);
|
||||||
|
else
|
||||||
|
sys_resp.stats[i] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
seq_puts(s, "System Power States\n");
|
||||||
|
seq_puts(s, "---------------------------------------------------\n");
|
||||||
|
seq_printf(s, "%-25s%-15s\n", "name", "count/time");
|
||||||
|
seq_puts(s, "---------------------------------------------------\n");
|
||||||
|
for (i = 0; i < MAX_CSTATE_ENTRIES; i++)
|
||||||
|
seq_printf(s, "%-25s%-20u\n",
|
||||||
|
system_req[i].name, sys_resp.stats[i]);
|
||||||
|
|
||||||
|
seq_puts(s, "\nCluster Power States\n");
|
||||||
|
seq_puts(s, "---------------------------------------------------\n");
|
||||||
|
seq_printf(s, "%-25s%-15s%-15s\n", "name", "phy-id", "count/time");
|
||||||
|
seq_puts(s, "---------------------------------------------------\n");
|
||||||
|
for (j = 0; j < MAX_CLUSTERS; j++) {
|
||||||
|
for (i = 0; i < MAX_CSTATE_ENTRIES; i++)
|
||||||
|
seq_printf(s, "%-25s%-15d%-20u\n",
|
||||||
|
cluster_req[i].name, j, cl_resp[j].stats[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
seq_puts(s, "\nCore Power States\n");
|
||||||
|
seq_puts(s, "-------------------------------------------------------------------\n");
|
||||||
|
seq_printf(s, "%-25s%-15s%-15s%-15s\n", "name", "mpidr-lin", "log-id", "count/time");
|
||||||
|
seq_puts(s, "-------------------------------------------------------------------\n");
|
||||||
|
for (j = 0; j < MAX_CPUS; j++) {
|
||||||
|
for (i = 0; i < MAX_CSTATE_ENTRIES; i++)
|
||||||
|
seq_printf(s, "%-25s%-15d%-15u%-20u\n",
|
||||||
|
core_req[i].name, j, core_resp[j].log_id,
|
||||||
|
core_resp[j].stats[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tegra23x_mce_dbg_cstats_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
int (*f)(struct seq_file *s, void *data);
|
||||||
|
|
||||||
|
f = tegra23x_mce_dbg_cstats_show;
|
||||||
|
return single_open(file, f, inode->i_private);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations tegra23x_mce_cstats_fops = {
|
||||||
|
.open = tegra23x_mce_dbg_cstats_open,
|
||||||
|
.read = seq_read,
|
||||||
|
.llseek = seq_lseek,
|
||||||
|
.release = single_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_SIMPLE_ATTRIBUTE(tegra23x_mce_versions_fops, tegra23x_mce_versions_get,
|
||||||
|
NULL, "%llx\n");
|
||||||
|
DEFINE_SIMPLE_ATTRIBUTE(tegra23x_mce_echo_fops, NULL,
|
||||||
|
tegra23x_mce_echo_set, "%llx\n");
|
||||||
|
|
||||||
|
struct debugfs_entry {
|
||||||
|
const char *name;
|
||||||
|
const struct file_operations *fops;
|
||||||
|
mode_t mode;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Make sure to put an NULL entry at the end of each group */
|
||||||
|
static struct debugfs_entry tegra23x_mce_attrs[] = {
|
||||||
|
{ "versions", &tegra23x_mce_versions_fops, 0444 },
|
||||||
|
{ "echo", &tegra23x_mce_echo_fops, 0200 },
|
||||||
|
{ "cstats", &tegra23x_mce_cstats_fops, 0444 },
|
||||||
|
{ NULL, NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct debugfs_entry *tegra_mce_attrs = tegra23x_mce_attrs;
|
||||||
|
|
||||||
|
static int tegra23x_mce_init(void)
|
||||||
|
{
|
||||||
|
struct debugfs_entry *fent;
|
||||||
|
struct dentry *dent;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
mce_debugfs = debugfs_create_dir("tegra_mce", NULL);
|
||||||
|
if (!mce_debugfs)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (fent = tegra_mce_attrs; fent->name; fent++) {
|
||||||
|
dent = debugfs_create_file(fent->name, fent->mode,
|
||||||
|
mce_debugfs, NULL, fent->fops);
|
||||||
|
if (IS_ERR_OR_NULL(dent)) {
|
||||||
|
ret = dent ? PTR_ERR(dent) : -EINVAL;
|
||||||
|
pr_err("%s: failed to create debugfs (%s): %d\n",
|
||||||
|
__func__, fent->name, ret);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_debug("%s: init finished\n", __func__);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err:
|
||||||
|
debugfs_remove_recursive(mce_debugfs);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tegra23x_mce_exit(void)
|
||||||
|
{
|
||||||
|
debugfs_remove_recursive(mce_debugfs);
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_DEBUG_FS */
|
||||||
|
|
||||||
|
static struct tegra_mce_ops t23x_mce_ops = {
|
||||||
|
.read_versions = tegra23x_mce_read_versions,
|
||||||
|
.read_l3_cache_ways = tegra23x_mce_read_l4_cache_ways,
|
||||||
|
.write_l3_cache_ways = tegra23x_mce_write_l4_cache_ways,
|
||||||
|
.echo_data = tegra23x_mce_echo_data,
|
||||||
|
.read_uncore_perfmon = tegra23x_mce_read_uncore_perfmon,
|
||||||
|
.write_uncore_perfmon = tegra23x_mce_write_uncore_perfmon,
|
||||||
|
.read_cstate_stats = tegra23x_mce_read_cstate_stats,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int t23x_mce_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
unsigned int cpu;
|
||||||
|
struct resource *res;
|
||||||
|
|
||||||
|
/* this ARI NS mapping applies to Split, Lock-step and FS */
|
||||||
|
for (cpu = 0; cpu < MAX_CPUS; cpu++) {
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, cpu);
|
||||||
|
ari_bar_array[cpu] = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(ari_bar_array[cpu])) {
|
||||||
|
dev_err(&pdev->dev, "mapping ARI failed for %d\n",
|
||||||
|
cpu);
|
||||||
|
return PTR_ERR(ari_bar_array[cpu]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tegra_mce_set_ops(&t23x_mce_ops);
|
||||||
|
#ifdef CONFIG_DEBUG_FS
|
||||||
|
tegra23x_mce_init();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int t23x_mce_remove(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_DEBUG_FS
|
||||||
|
tegra23x_mce_exit();
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id t23x_mce_of_match[] = {
|
||||||
|
{ .compatible = "nvidia,t23x-mce", .data = NULL },
|
||||||
|
{ },
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(of, t23x_mce_of_match);
|
||||||
|
|
||||||
|
static struct platform_driver t23x_mce_driver = {
|
||||||
|
.probe = t23x_mce_probe,
|
||||||
|
.remove = t23x_mce_remove,
|
||||||
|
.driver = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.name = "t23x-mce",
|
||||||
|
.of_match_table = of_match_ptr(t23x_mce_of_match),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(t23x_mce_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("NVIDIA Tegra23x MCE driver");
|
||||||
|
MODULE_AUTHOR("NVIDIA Corporation");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
||||||
91
include/linux/platform/tegra/t23x_ari.h
Normal file
91
include/linux/platform/tegra/t23x_ari.h
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
// Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||||
|
|
||||||
|
#ifndef T23X_ARI_H
|
||||||
|
#define T23X_ARI_H
|
||||||
|
|
||||||
|
/* ARI Version numbers */
|
||||||
|
#define TEGRA_ARI_VERSION_MAJOR 8UL
|
||||||
|
#define TEGRA_ARI_VERSION_MINOR 1UL
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ARI Request IDs
|
||||||
|
*
|
||||||
|
* TODO: RENUMBER range before finalization
|
||||||
|
* NOTE: for documentation purposes, only documenting gaps
|
||||||
|
* in ranges, to indicate that we know about the missing ids
|
||||||
|
*
|
||||||
|
* Require NO LAB Locks
|
||||||
|
* range from 0 - 31
|
||||||
|
*/
|
||||||
|
#define TEGRA_ARI_VERSION 0UL
|
||||||
|
#define TEGRA_ARI_ECHO 1UL
|
||||||
|
#define TEGRA_ARI_NUM_CORES 2UL
|
||||||
|
#define TEGRA_ARI_CSTATE_STAT_QUERY 3UL
|
||||||
|
/* Undefined 4 - 28 */
|
||||||
|
/* Debug Only ARIs at the end of the NO LAB Lock Range */
|
||||||
|
#define TEGRA_ARI_CORE_DEBUG_RECOVERY 29UL
|
||||||
|
#define TEGRA_ARI_DSU_DEBUG_RECOVERY 30UL
|
||||||
|
#define TEGRA_ARI_CLUSTER_WARM_RESET 31UL
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Require CORE LAB Lock -- obtained by MTM from ARI
|
||||||
|
* range from 32 - 63
|
||||||
|
*/
|
||||||
|
/* UNDEFINED 32 */
|
||||||
|
/* UNDEFINED 33 */
|
||||||
|
#define TEGRA_ARI_ONLINE_CORE 34UL
|
||||||
|
#define TEGRA_ARI_ENTER_CSTATE 35UL
|
||||||
|
/* UNDEFINED 36 */
|
||||||
|
#define TEGRA_ARI_TRIGGER_ONLINE_IST 37UL
|
||||||
|
/*
|
||||||
|
* Require CLUSTER and CORE LAB Lock -- obtained by MTM from ARI
|
||||||
|
* range from 64 - 95
|
||||||
|
*/
|
||||||
|
/* UNDEFINED 64 */
|
||||||
|
#define TEGRA_ARI_NVFREQ_REQ 65UL
|
||||||
|
#define TEGRA_ARI_NVFREQ_FEEDBACK 66UL
|
||||||
|
#define TEGRA_ARI_CLUSTER_ATCLKEN 67UL
|
||||||
|
/*
|
||||||
|
* Require CCPLEX, CLUSTER and CORE LAB Lock -- obtained by MTM from ARI
|
||||||
|
* range from 96 - 127
|
||||||
|
*/
|
||||||
|
#define TEGRA_ARI_CCPLEX_CACHE_CONTROL 96UL
|
||||||
|
#define TEGRA_ARI_CCPLEX_CACHE_CLEAN 97UL
|
||||||
|
/* UNDEFINED 98 */
|
||||||
|
#define TEGRA_ARI_CCPLEX_LATIC_ON 99UL
|
||||||
|
#define TEGRA_ARI_UPDATE_CROSSOVER 100UL
|
||||||
|
/* UNDEFINED 101 */
|
||||||
|
#define TEGRA_ARI_CCPLEX_SHUTDOWN 102UL
|
||||||
|
/* UNDEFINED 103 */
|
||||||
|
#define TEGRA_ARI_CSTATE_INFO 104UL
|
||||||
|
#define TEGRA_ARI_IS_SC7_ALLOWED 105UL
|
||||||
|
/* UNDEFINED 106 */
|
||||||
|
/* UNDEFINED 107 */
|
||||||
|
#define TEGRA_ARI_SECURITY_CONFIG 108UL
|
||||||
|
#define TEGRA_ARI_UPDATE_CCPLEX_CARVEOUTS 109UL
|
||||||
|
#define TEGRA_ARI_DDA_CONTROL 110UL
|
||||||
|
#define TEGRA_ARI_PERFMON 111UL
|
||||||
|
#define TEGRA_ARI_DEBUG_CONFIG 112UL
|
||||||
|
#define TEGRA_ARI_CCPLEX_ERROR_RECOVERY_RESET 114UL
|
||||||
|
|
||||||
|
/* Values for ARI CSTATE STAT QUERY */
|
||||||
|
#define TEGRA_ARI_STAT_QUERY_SC7_ENTRIES 1UL
|
||||||
|
#define TEGRA_ARI_STAT_QUERY_CC7_ENTRIES 6UL
|
||||||
|
#define TEGRA_ARI_STAT_QUERY_C7_ENTRIES 14UL
|
||||||
|
#define TEGRA_ARI_STAT_QUERY_SC7_ENTRY_TIME_SUM 60UL
|
||||||
|
#define TEGRA_ARI_STAT_QUERY_CC7_ENTRY_TIME_SUM 61UL
|
||||||
|
#define TEGRA_ARI_STAT_QUERY_C7_ENTRY_TIME_SUM 64UL
|
||||||
|
#define TEGRA_ARI_STAT_QUERY_SC7_EXIT_TIME_SUM 70UL
|
||||||
|
#define TEGRA_ARI_STAT_QUERY_CC7_EXIT_TIME_SUM 71UL
|
||||||
|
#define TEGRA_ARI_STAT_QUERY_C7_EXIT_TIME_SUM 74UL
|
||||||
|
|
||||||
|
/* Values for ARI UPDATE CROSSOVER */
|
||||||
|
#define TEGRA_ARI_CROSSOVER_C7_LOWER_BOUND 0UL
|
||||||
|
#define TEGRA_ARI_CROSSOVER_CC7_LOWER_BOUND 1UL
|
||||||
|
|
||||||
|
/* Values for ARI UPDATE CCPLEX CARVEOUTS */
|
||||||
|
#define TEGRA_ARI_UPDATE_CCPLEX_CARVEOUTS_ALL 0UL
|
||||||
|
|
||||||
|
#endif /* T23X_ARI_H */
|
||||||
148
include/linux/platform/tegra/tegra-mce.h
Normal file
148
include/linux/platform/tegra/tegra-mce.h
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
// Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
||||||
|
|
||||||
|
#ifndef _LINUX_TEGRA_MCE_H
|
||||||
|
#define _LINUX_TEGRA_MCE_H
|
||||||
|
|
||||||
|
/* NOTE:
|
||||||
|
* For correct version validation, below two defines need to be
|
||||||
|
* updated whenever there is a new ARI implementation.
|
||||||
|
*/
|
||||||
|
#define CUR_ARI_VER_MAJOR 1
|
||||||
|
#define CUR_ARI_VER_MINOR 2
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TEGRA_MCE_XOVER_C1_C6, /* Only valid for Denver */
|
||||||
|
TEGRA_MCE_XOVER_CC1_CC6,
|
||||||
|
TEGRA_MCE_XOVER_CC1_CC7,
|
||||||
|
|
||||||
|
TEGRA_MCE_XOVER_MAX = TEGRA_MCE_XOVER_CC1_CC7
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TEGRA_MCE_CSTATS_CLEAR,
|
||||||
|
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_SC7,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_SC4,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_SC3,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_SC2,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_CCP3,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_A57_CC6,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_A57_CC7,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_D15_CC6,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_D15_CC7,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_D15_CORE0_C6,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_D15_CORE1_C6,
|
||||||
|
/* RESV: 12-13 */
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_D15_CORE0_C7 = 14,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_D15_CORE1_C7,
|
||||||
|
/* RESV: 16-17 */
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_A57_CORE0_C7 = 18,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_A57_CORE1_C7,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_A57_CORE2_C7,
|
||||||
|
TEGRA_MCE_CSTATS_ENTRIES_A57_CORE3_C7,
|
||||||
|
TEGRA_MCE_CSTATS_LAST_ENTRY_D15_CORE0,
|
||||||
|
TEGRA_MCE_CSTATS_LAST_ENTRY_D15_CORE1,
|
||||||
|
/* RESV: 24-25 */
|
||||||
|
TEGRA_MCE_CSTATS_LAST_ENTRY_A57_CORE0,
|
||||||
|
TEGRA_MCE_CSTATS_LAST_ENTRY_A57_CORE1,
|
||||||
|
TEGRA_MCE_CSTATS_LAST_ENTRY_A57_CORE2,
|
||||||
|
TEGRA_MCE_CSTATS_LAST_ENTRY_A57_CORE3,
|
||||||
|
|
||||||
|
TEGRA_MCE_CSTATS_MAX = TEGRA_MCE_CSTATS_LAST_ENTRY_A57_CORE3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TEGRA_MCE_ENUM_D15_CORE0,
|
||||||
|
TEGRA_MCE_D15_CORE1,
|
||||||
|
/* RESV: 2-3 */
|
||||||
|
TEGRA_MCE_ENUM_A57_0 = 4,
|
||||||
|
TEGRA_MCE_ENUM_A57_1,
|
||||||
|
TEGRA_MCE_ENUM_A57_2,
|
||||||
|
TEGRA_MCE_ENUM_A57_3,
|
||||||
|
|
||||||
|
TEGRA_MCE_ENUM_MAX = TEGRA_MCE_ENUM_A57_3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TEGRA_MCE_FEATURE_CCP3,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* MCA support */
|
||||||
|
typedef union {
|
||||||
|
struct {
|
||||||
|
u8 cmd;
|
||||||
|
u8 subidx;
|
||||||
|
u8 idx;
|
||||||
|
u8 inst;
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
u32 low;
|
||||||
|
u32 high;
|
||||||
|
};
|
||||||
|
u64 data;
|
||||||
|
} mca_cmd_t;
|
||||||
|
|
||||||
|
/* NOTE: These functions will return -ENOTSUPP if no implementation */
|
||||||
|
int tegra_mce_enter_cstate(u32 state, u32 wake_time);
|
||||||
|
int tegra_mce_update_cstate_info(u32 cluster, u32 ccplex, u32 system,
|
||||||
|
u8 force, u32 wake_mask, bool valid);
|
||||||
|
int tegra_mce_update_crossover_time(u32 type, u32 time);
|
||||||
|
int tegra_mce_read_cstate_stats(u32 state, u64 *stats);
|
||||||
|
int tegra_mce_write_cstate_stats(u32 state, u32 stats);
|
||||||
|
int tegra_mce_is_sc7_allowed(u32 state, u32 wake, u32 *allowed);
|
||||||
|
int tegra_mce_online_core(int cpu);
|
||||||
|
int tegra_mce_cc3_ctrl(u32 ndiv, u32 vindex, u8 enable);
|
||||||
|
int tegra_mce_echo_data(u64 data, u64 *matched);
|
||||||
|
int tegra_mce_read_versions(u32 *major, u32 *minor);
|
||||||
|
int tegra_mce_enum_features(u64 *features);
|
||||||
|
int tegra_mce_read_uncore_mca(mca_cmd_t cmd, u64 *data, u32 *error);
|
||||||
|
int tegra_mce_write_uncore_mca(mca_cmd_t cmd, u64 data, u32 *error);
|
||||||
|
int tegra_mce_read_uncore_perfmon(u32 req, u32 *data);
|
||||||
|
int tegra_mce_write_uncore_perfmon(u32 req, u32 data);
|
||||||
|
int tegra_mce_enable_latic(void);
|
||||||
|
int tegra_mce_write_dda_ctrl(u32 index, u64 value);
|
||||||
|
int tegra_mce_read_dda_ctrl(u32 index, u64 *value);
|
||||||
|
|
||||||
|
/* L3 cache ways read/write functions */
|
||||||
|
int tegra_mce_read_l3_cache_ways(u64 *value);
|
||||||
|
int tegra_mce_write_l3_cache_ways(u64 data, u64 *value);
|
||||||
|
|
||||||
|
int tegra_mce_read_rt_safe_mask(u64 *);
|
||||||
|
int tegra_mce_write_rt_safe_mask(u64);
|
||||||
|
int tegra_mce_read_rt_window_us(u64 *);
|
||||||
|
int tegra_mce_write_rt_window_us(u64);
|
||||||
|
int tegra_mce_read_rt_fwd_progress_us(u64 *);
|
||||||
|
int tegra_mce_write_rt_fwd_progress_us(u64);
|
||||||
|
|
||||||
|
struct tegra_mce_ops {
|
||||||
|
int (*enter_cstate)(u32, u32);
|
||||||
|
int (*update_cstate_info)(u32, u32, u32, u8, u32, bool);
|
||||||
|
int (*update_crossover_time)(u32, u32);
|
||||||
|
int (*read_cstate_stats)(u32, u64 *);
|
||||||
|
int (*write_cstate_stats)(u32, u32);
|
||||||
|
int (*is_sc7_allowed)(u32, u32, u32 *);
|
||||||
|
int (*online_core)(int);
|
||||||
|
int (*cc3_ctrl)(u32, u32, u8);
|
||||||
|
int (*echo_data)(u64, u64*);
|
||||||
|
int (*read_versions)(u32 *, u32 *);
|
||||||
|
int (*enum_features)(u64 *);
|
||||||
|
int (*read_uncore_mca)(mca_cmd_t, u64 *, u32 *);
|
||||||
|
int (*write_uncore_mca)(mca_cmd_t, u64, u32 *);
|
||||||
|
int (*read_uncore_perfmon)(u32, u32 *);
|
||||||
|
int (*write_uncore_perfmon)(u32, u32);
|
||||||
|
int (*enable_latic)(void);
|
||||||
|
int (*write_dda_ctrl)(u32 index, u64 value);
|
||||||
|
int (*read_dda_ctrl)(u32 index, u64 *value);
|
||||||
|
int (*read_l3_cache_ways)(u64 *value);
|
||||||
|
int (*write_l3_cache_ways)(u64 data, u64 *value);
|
||||||
|
int (*read_rt_safe_mask)(u64 *);
|
||||||
|
int (*write_rt_safe_mask)(u64);
|
||||||
|
int (*read_rt_window_us)(u64 *);
|
||||||
|
int (*write_rt_window_us)(u64);
|
||||||
|
int (*read_rt_fwd_progress_us)(u64 *);
|
||||||
|
int (*write_rt_fwd_progress_us)(u64);
|
||||||
|
};
|
||||||
|
|
||||||
|
void tegra_mce_set_ops(struct tegra_mce_ops *);
|
||||||
|
#endif /* _LINUX_TEGRA_MCE_H */
|
||||||
Reference in New Issue
Block a user