mirror of
git://nv-tegra.nvidia.com/linux-hwpm.git
synced 2025-12-22 09:12:05 +03:00
tegra: hwpm: add clk-rst HALs, update HAL validation
- Make clock reset functions into HALs. This way we can control clock-reset logic for any chip. Set clock-reset HAL pointers to appropriate functions. - Remove clock-reset function wrappers as these will not be required and corresponding HAL pointers will be used. - As clock reset init is defined as a HAL, modify probe logic to initialize chip info before invoking any HALs. - Move common/primary HAL validation logic to common code and implement new HAL to validate chip specific HALs. This way we can ensure that HAL pointers are set as expected. - Keep only one definition for t234_hwpm_init_chip_info as t234 should always be initialized and hence only single definition should be available. - Expected return value of 0 indicates success and any other value (mostly negative in current logic) indicates error, compare function returns with 0 to print error in tegra_hwpm_release(). - Since a build can support both ACPI and device tree, update init_chip_info() to retrieve chip information from ACPI and device tree in case of failure. Jira THWPM-41 Bug 3583624 Change-Id: I03fefae0b3b0c8ce46d175d39e4fdbb45e2bb22f Signed-off-by: Vedashree Vidwans <vvidwans@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvidia/+/2789668 Reviewed-by: svc-mobile-coverity <svc-mobile-coverity@nvidia.com> Reviewed-by: svc-mobile-cert <svc-mobile-cert@nvidia.com> Reviewed-by: Vasuki Shankar <vasukis@nvidia.com> Reviewed-by: Seema Khowala <seemaj@nvidia.com> GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-hwpm/+/2797445
This commit is contained in:
committed by
mobile promotions
parent
913f1b0697
commit
5166c3ab71
@@ -222,3 +222,172 @@ void tegra_hwpm_release_sw_setup(struct tegra_soc_hwpm *hwpm)
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Validate HALs that are expected to be populated for each chip */
|
||||||
|
bool tegra_hwpm_validate_primary_hals(struct tegra_soc_hwpm *hwpm)
|
||||||
|
{
|
||||||
|
tegra_hwpm_fn(hwpm, " ");
|
||||||
|
|
||||||
|
if (hwpm->active_chip->is_ip_active == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "is_ip_active HAL uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->is_resource_active == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "is_resource_active HAL uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->get_rtr_int_idx == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "get_rtr_int_idx HAL uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->get_ip_max_idx == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "get_ip_max_idx HAL uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->extract_ip_ops == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "extract_ip_ops uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->force_enable_ips == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "force_enable_ips uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->validate_current_config == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "validate_current_config uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->get_fs_info == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "get_fs_info uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->get_resource_info == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "get_resource_info uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->init_prod_values == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "init_prod_values uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->disable_cg == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "disable_cg uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->enable_cg == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "enable_cg uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->reserve_rtr == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "reserve_rtr uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->release_rtr == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "release_rtr uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->perfmon_enable == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "perfmon_enable HAL uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->perfmon_disable == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "perfmon_disable HAL uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->perfmux_disable == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "perfmux_disable HAL uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->disable_triggers == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "disable_triggers uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->check_status == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "check_status uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->disable_mem_mgmt == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "disable_mem_mgmt HAL uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->enable_mem_mgmt == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "enable_mem_mgmt HAL uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->invalidate_mem_config == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "invalidate_mem_config HAL uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->stream_mem_bytes == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "stream_mem_bytes uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->disable_pma_streaming == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "disable_pma_streaming uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->update_mem_bytes_get_ptr == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "update_mem_bytes_get_ptr uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->get_mem_bytes_put_ptr == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "get_mem_bytes_put_ptr uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->membuf_overflow_status == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "membuf_overflow_status uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->get_alist_buf_size == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "alist_buf_size uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->zero_alist_regs == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "zero_alist_regs HAL uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->copy_alist == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "copy_alist HAL uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->check_alist == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "check_alist uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->validate_secondary_hals == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm,
|
||||||
|
"validate_secondary_hals HAL uninitialized");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hwpm->active_chip->validate_secondary_hals(hwpm);
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,13 +16,6 @@
|
|||||||
|
|
||||||
struct tegra_soc_hwpm;
|
struct tegra_soc_hwpm;
|
||||||
|
|
||||||
#ifdef CONFIG_TEGRA_T234_HWPM
|
|
||||||
int t234_hwpm_init_chip_info(struct tegra_soc_hwpm *hwpm);
|
int t234_hwpm_init_chip_info(struct tegra_soc_hwpm *hwpm);
|
||||||
#else
|
|
||||||
int t234_hwpm_init_chip_info(struct tegra_soc_hwpm *hwpm)
|
|
||||||
{
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* T234_HWPM_INIT_H */
|
#endif /* T234_HWPM_INIT_H */
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
* more details.
|
* more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <tegra_hwpm_clk_rst.h>
|
||||||
#include <tegra_hwpm_common.h>
|
#include <tegra_hwpm_common.h>
|
||||||
#include <tegra_hwpm_kmem.h>
|
#include <tegra_hwpm_kmem.h>
|
||||||
#include <tegra_hwpm_log.h>
|
#include <tegra_hwpm_log.h>
|
||||||
@@ -23,6 +24,13 @@ static struct tegra_soc_hwpm_chip t234_chip_info = {
|
|||||||
.chip_ips = NULL,
|
.chip_ips = NULL,
|
||||||
|
|
||||||
/* HALs */
|
/* HALs */
|
||||||
|
.validate_secondary_hals = t234_hwpm_validate_secondary_hals,
|
||||||
|
|
||||||
|
.clk_rst_prepare = tegra_hwpm_clk_rst_prepare,
|
||||||
|
.clk_rst_set_rate_enable = tegra_hwpm_clk_rst_set_rate_enable,
|
||||||
|
.clk_rst_disable = tegra_hwpm_clk_rst_disable,
|
||||||
|
.clk_rst_release = tegra_hwpm_clk_rst_release,
|
||||||
|
|
||||||
.is_ip_active = t234_hwpm_is_ip_active,
|
.is_ip_active = t234_hwpm_is_ip_active,
|
||||||
.is_resource_active = t234_hwpm_is_resource_active,
|
.is_resource_active = t234_hwpm_is_resource_active,
|
||||||
|
|
||||||
@@ -63,160 +71,28 @@ static struct tegra_soc_hwpm_chip t234_chip_info = {
|
|||||||
.check_alist = tegra_hwpm_check_alist,
|
.check_alist = tegra_hwpm_check_alist,
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool t234_hwpm_validate_hals(struct tegra_soc_hwpm *hwpm)
|
bool t234_hwpm_validate_secondary_hals(struct tegra_soc_hwpm *hwpm)
|
||||||
{
|
{
|
||||||
if (hwpm->active_chip->is_ip_active == NULL) {
|
tegra_hwpm_fn(hwpm, " ");
|
||||||
tegra_hwpm_err(hwpm, "is_ip_active HAL uninitialized");
|
|
||||||
|
if (hwpm->active_chip->clk_rst_prepare == NULL) {
|
||||||
|
tegra_hwpm_err(hwpm, "clk_rst_prepare HAL uninitialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hwpm->active_chip->is_resource_active == NULL) {
|
if (hwpm->active_chip->clk_rst_set_rate_enable == NULL) {
|
||||||
tegra_hwpm_err(hwpm, "is_resource_active HAL uninitialized");
|
tegra_hwpm_err(hwpm,
|
||||||
|
"clk_rst_set_rate_enable HAL uninitialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hwpm->active_chip->get_rtr_int_idx == NULL) {
|
if (hwpm->active_chip->clk_rst_disable == NULL) {
|
||||||
tegra_hwpm_err(hwpm, "get_rtr_int_idx HAL uninitialized");
|
tegra_hwpm_err(hwpm, "clk_rst_disable HAL uninitialized");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hwpm->active_chip->get_ip_max_idx == NULL) {
|
if (hwpm->active_chip->clk_rst_release == NULL) {
|
||||||
tegra_hwpm_err(hwpm, "get_ip_max_idx HAL uninitialized");
|
tegra_hwpm_err(hwpm, "clk_rst_release HAL uninitialized");
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->extract_ip_ops == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "extract_ip_ops uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->force_enable_ips == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "force_enable_ips uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->validate_current_config == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "validate_current_config uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->get_fs_info == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "get_fs_info uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->get_resource_info == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "get_resource_info uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->init_prod_values == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "init_prod_values uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->disable_cg == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "disable_cg uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->enable_cg == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "enable_cg uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->reserve_rtr == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "reserve_rtr uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->release_rtr == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "release_rtr uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->perfmon_enable == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "perfmon_enable HAL uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->perfmon_disable == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "perfmon_disable HAL uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->perfmux_disable == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "perfmux_disable HAL uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->disable_triggers == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "disable_triggers uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->check_status == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "check_status uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->disable_mem_mgmt == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "disable_mem_mgmt HAL uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->enable_mem_mgmt == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "enable_mem_mgmt HAL uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->invalidate_mem_config == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "invalidate_mem_config HAL uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->stream_mem_bytes == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "stream_mem_bytes uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->disable_pma_streaming == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "disable_pma_streaming uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->update_mem_bytes_get_ptr == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "update_mem_bytes_get_ptr uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->get_mem_bytes_put_ptr == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "get_mem_bytes_put_ptr uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->membuf_overflow_status == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "membuf_overflow_status uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->get_alist_buf_size == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "alist_buf_size uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->zero_alist_regs == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "zero_alist_regs HAL uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->copy_alist == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "copy_alist HAL uninitialized");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hwpm->active_chip->check_alist == NULL) {
|
|
||||||
tegra_hwpm_err(hwpm, "check_alist uninitialized");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,7 +376,7 @@ int t234_hwpm_init_chip_info(struct tegra_soc_hwpm *hwpm)
|
|||||||
t234_active_ip_info[T234_HWPM_IP_VIC] = &t234_hwpm_ip_vic;
|
t234_active_ip_info[T234_HWPM_IP_VIC] = &t234_hwpm_ip_vic;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!t234_hwpm_validate_hals(hwpm)) {
|
if (!tegra_hwpm_validate_primary_hals(hwpm)) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ enum tegra_soc_hwpm_resource;
|
|||||||
struct tegra_soc_hwpm;
|
struct tegra_soc_hwpm;
|
||||||
struct hwpm_ip_aperture;
|
struct hwpm_ip_aperture;
|
||||||
|
|
||||||
|
bool t234_hwpm_validate_secondary_hals(struct tegra_soc_hwpm *hwpm);
|
||||||
bool t234_hwpm_is_ip_active(struct tegra_soc_hwpm *hwpm,
|
bool t234_hwpm_is_ip_active(struct tegra_soc_hwpm *hwpm,
|
||||||
u32 ip_enum, u32 *config_ip_index);
|
u32 ip_enum, u32 *config_ip_index);
|
||||||
bool t234_hwpm_is_resource_active(struct tegra_soc_hwpm *hwpm,
|
bool t234_hwpm_is_resource_active(struct tegra_soc_hwpm *hwpm,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#define TEGRA_HWPM_FUSE_SECURITY_MODE_MASK BIT(1)
|
#define TEGRA_HWPM_FUSE_SECURITY_MODE_MASK BIT(1)
|
||||||
#define TEGRA_HWPM_FUSE_HWPM_GLOBAL_DISABLE_MASK BIT(2)
|
#define TEGRA_HWPM_FUSE_HWPM_GLOBAL_DISABLE_MASK BIT(2)
|
||||||
|
|
||||||
|
struct tegra_hwpm_os_linux;
|
||||||
struct tegra_hwpm_mem_mgmt;
|
struct tegra_hwpm_mem_mgmt;
|
||||||
struct tegra_hwpm_allowlist_map;
|
struct tegra_hwpm_allowlist_map;
|
||||||
enum tegra_soc_hwpm_ip_reg_op;
|
enum tegra_soc_hwpm_ip_reg_op;
|
||||||
@@ -372,6 +373,13 @@ struct tegra_soc_hwpm_chip {
|
|||||||
struct hwpm_ip **chip_ips;
|
struct hwpm_ip **chip_ips;
|
||||||
|
|
||||||
/* Chip HALs */
|
/* Chip HALs */
|
||||||
|
bool (*validate_secondary_hals)(struct tegra_soc_hwpm *hwpm);
|
||||||
|
|
||||||
|
int (*clk_rst_prepare)(struct tegra_hwpm_os_linux *hwpm_linux);
|
||||||
|
int (*clk_rst_set_rate_enable)(struct tegra_hwpm_os_linux *hwpm_linux);
|
||||||
|
int (*clk_rst_disable)(struct tegra_hwpm_os_linux *hwpm_linux);
|
||||||
|
void (*clk_rst_release)(struct tegra_hwpm_os_linux *hwpm_linux);
|
||||||
|
|
||||||
bool (*is_ip_active)(struct tegra_soc_hwpm *hwpm,
|
bool (*is_ip_active)(struct tegra_soc_hwpm *hwpm,
|
||||||
u32 ip_enum, u32 *config_ip_index);
|
u32 ip_enum, u32 *config_ip_index);
|
||||||
bool (*is_resource_active)(struct tegra_soc_hwpm *hwpm,
|
bool (*is_resource_active)(struct tegra_soc_hwpm *hwpm,
|
||||||
|
|||||||
@@ -23,36 +23,23 @@
|
|||||||
#else
|
#else
|
||||||
struct tegra_hwpm_os_linux;
|
struct tegra_hwpm_os_linux;
|
||||||
|
|
||||||
int tegra_hwpm_clk_rst_prepare_impl(struct tegra_hwpm_os_linux *hwpm_linux)
|
int tegra_hwpm_clk_rst_prepare(struct tegra_hwpm_os_linux *hwpm_linux)
|
||||||
{
|
{
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tegra_hwpm_clk_rst_set_rate_enable_impl(
|
int tegra_hwpm_clk_rst_set_rate_enable(struct tegra_hwpm_os_linux *hwpm_linux)
|
||||||
struct tegra_hwpm_os_linux *hwpm_linux)
|
|
||||||
{
|
{
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int tegra_hwpm_clk_rst_disable_impl(struct tegra_hwpm_os_linux *hwpm_linux)
|
int tegra_hwpm_clk_rst_disable(struct tegra_hwpm_os_linux *hwpm_linux)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void tegra_hwpm_clk_rst_release_impl(struct tegra_hwpm_os_linux *hwpm_linux)
|
void tegra_hwpm_clk_rst_release(struct tegra_hwpm_os_linux *hwpm_linux)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define tegra_hwpm_clk_rst_prepare(hwpm_linux) \
|
|
||||||
tegra_hwpm_clk_rst_prepare_impl(hwpm_linux)
|
|
||||||
|
|
||||||
#define tegra_hwpm_clk_rst_set_rate_enable(hwpm_linux) \
|
|
||||||
tegra_hwpm_clk_rst_set_rate_enable_impl(hwpm_linux)
|
|
||||||
|
|
||||||
#define tegra_hwpm_clk_rst_disable(hwpm_linux) \
|
|
||||||
tegra_hwpm_clk_rst_disable_impl(hwpm_linux)
|
|
||||||
|
|
||||||
#define tegra_hwpm_clk_rst_release(hwpm_linux) \
|
|
||||||
tegra_hwpm_clk_rst_release_impl(hwpm_linux)
|
|
||||||
|
|
||||||
#endif /* TEGRA_HWPM_CLK_RST_H */
|
#endif /* TEGRA_HWPM_CLK_RST_H */
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ int tegra_hwpm_copy_alist(struct tegra_soc_hwpm *hwpm,
|
|||||||
bool tegra_hwpm_check_alist(struct tegra_soc_hwpm *hwpm,
|
bool tegra_hwpm_check_alist(struct tegra_soc_hwpm *hwpm,
|
||||||
struct hwpm_ip_aperture *aperture, u64 phys_addr);
|
struct hwpm_ip_aperture *aperture, u64 phys_addr);
|
||||||
|
|
||||||
|
bool tegra_hwpm_validate_primary_hals(struct tegra_soc_hwpm *hwpm);
|
||||||
int tegra_hwpm_setup_hw(struct tegra_soc_hwpm *hwpm);
|
int tegra_hwpm_setup_hw(struct tegra_soc_hwpm *hwpm);
|
||||||
int tegra_hwpm_setup_sw(struct tegra_soc_hwpm *hwpm);
|
int tegra_hwpm_setup_sw(struct tegra_soc_hwpm *hwpm);
|
||||||
int tegra_hwpm_disable_triggers(struct tegra_soc_hwpm *hwpm);
|
int tegra_hwpm_disable_triggers(struct tegra_soc_hwpm *hwpm);
|
||||||
|
|||||||
@@ -26,15 +26,8 @@
|
|||||||
|
|
||||||
#define LA_CLK_RATE 625000000UL
|
#define LA_CLK_RATE 625000000UL
|
||||||
|
|
||||||
int tegra_hwpm_clk_rst_prepare_impl(struct tegra_hwpm_os_linux *hwpm_linux)
|
int tegra_hwpm_clk_rst_prepare(struct tegra_hwpm_os_linux *hwpm_linux)
|
||||||
{
|
{
|
||||||
#if defined(CONFIG_TEGRA_HWPM_OOT)
|
|
||||||
/*
|
|
||||||
* Starting OOT config, clocks and resets are handled by UEFI layer
|
|
||||||
* Hence, do nothing.
|
|
||||||
*/
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct tegra_soc_hwpm *hwpm = &hwpm_linux->hwpm;
|
struct tegra_soc_hwpm *hwpm = &hwpm_linux->hwpm;
|
||||||
|
|
||||||
@@ -73,19 +66,10 @@ int tegra_hwpm_clk_rst_prepare_impl(struct tegra_hwpm_os_linux *hwpm_linux)
|
|||||||
|
|
||||||
fail:
|
fail:
|
||||||
return ret;
|
return ret;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int tegra_hwpm_clk_rst_set_rate_enable_impl(
|
int tegra_hwpm_clk_rst_set_rate_enable(struct tegra_hwpm_os_linux *hwpm_linux)
|
||||||
struct tegra_hwpm_os_linux *hwpm_linux)
|
|
||||||
{
|
{
|
||||||
#if defined(CONFIG_TEGRA_HWPM_OOT)
|
|
||||||
/*
|
|
||||||
* Starting OOT config, clocks and resets are handled by UEFI layer
|
|
||||||
* Hence, do nothing.
|
|
||||||
*/
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct tegra_soc_hwpm *hwpm = &hwpm_linux->hwpm;
|
struct tegra_soc_hwpm *hwpm = &hwpm_linux->hwpm;
|
||||||
|
|
||||||
@@ -135,18 +119,10 @@ int tegra_hwpm_clk_rst_set_rate_enable_impl(
|
|||||||
|
|
||||||
fail:
|
fail:
|
||||||
return ret;
|
return ret;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int tegra_hwpm_clk_rst_disable_impl(struct tegra_hwpm_os_linux *hwpm_linux)
|
int tegra_hwpm_clk_rst_disable(struct tegra_hwpm_os_linux *hwpm_linux)
|
||||||
{
|
{
|
||||||
#if defined(CONFIG_TEGRA_HWPM_OOT)
|
|
||||||
/*
|
|
||||||
* Starting OOT config, clocks and resets are handled by UEFI layer
|
|
||||||
* Hence, do nothing.
|
|
||||||
*/
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct tegra_soc_hwpm *hwpm = &hwpm_linux->hwpm;
|
struct tegra_soc_hwpm *hwpm = &hwpm_linux->hwpm;
|
||||||
|
|
||||||
@@ -165,17 +141,10 @@ int tegra_hwpm_clk_rst_disable_impl(struct tegra_hwpm_os_linux *hwpm_linux)
|
|||||||
}
|
}
|
||||||
fail:
|
fail:
|
||||||
return ret;
|
return ret;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void tegra_hwpm_clk_rst_release_impl(struct tegra_hwpm_os_linux *hwpm_linux)
|
void tegra_hwpm_clk_rst_release(struct tegra_hwpm_os_linux *hwpm_linux)
|
||||||
{
|
{
|
||||||
#if defined(CONFIG_TEGRA_HWPM_OOT)
|
|
||||||
/*
|
|
||||||
* Starting OOT config, clocks and resets are handled by UEFI layer
|
|
||||||
* Hence, do nothing.
|
|
||||||
*/
|
|
||||||
#else
|
|
||||||
if (tegra_hwpm_is_platform_silicon()) {
|
if (tegra_hwpm_is_platform_silicon()) {
|
||||||
if (hwpm_linux->la_clk) {
|
if (hwpm_linux->la_clk) {
|
||||||
devm_clk_put(hwpm_linux->dev, hwpm_linux->la_clk);
|
devm_clk_put(hwpm_linux->dev, hwpm_linux->la_clk);
|
||||||
@@ -191,5 +160,4 @@ void tegra_hwpm_clk_rst_release_impl(struct tegra_hwpm_os_linux *hwpm_linux)
|
|||||||
reset_control_assert(hwpm_linux->hwpm_rst);
|
reset_control_assert(hwpm_linux->hwpm_rst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,9 @@
|
|||||||
|
|
||||||
struct tegra_hwpm_os_linux;
|
struct tegra_hwpm_os_linux;
|
||||||
|
|
||||||
int tegra_hwpm_clk_rst_prepare_impl(struct tegra_hwpm_os_linux *hwpm_linux);
|
int tegra_hwpm_clk_rst_prepare(struct tegra_hwpm_os_linux *hwpm_linux);
|
||||||
int tegra_hwpm_clk_rst_set_rate_enable_impl(
|
int tegra_hwpm_clk_rst_set_rate_enable(struct tegra_hwpm_os_linux *hwpm_linux);
|
||||||
struct tegra_hwpm_os_linux *hwpm_linux);
|
int tegra_hwpm_clk_rst_disable(struct tegra_hwpm_os_linux *hwpm_linux);
|
||||||
int tegra_hwpm_clk_rst_disable_impl(struct tegra_hwpm_os_linux *hwpm_linux);
|
void tegra_hwpm_clk_rst_release(struct tegra_hwpm_os_linux *hwpm_linux);
|
||||||
void tegra_hwpm_clk_rst_release_impl(struct tegra_hwpm_os_linux *hwpm_linux);
|
|
||||||
|
|
||||||
#endif /* TEGRA_HWPM_OS_LINUX_CLK_RST_UTILS_H */
|
#endif /* TEGRA_HWPM_OS_LINUX_CLK_RST_UTILS_H */
|
||||||
|
|||||||
@@ -166,13 +166,6 @@ static int tegra_hwpm_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
(void) dma_set_mask_and_coherent(hwpm_linux->dev, DMA_BIT_MASK(39));
|
(void) dma_set_mask_and_coherent(hwpm_linux->dev, DMA_BIT_MASK(39));
|
||||||
|
|
||||||
ret = tegra_hwpm_clk_rst_prepare(hwpm_linux);
|
|
||||||
if (ret != 0) {
|
|
||||||
goto clock_reset_fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
tegra_hwpm_debugfs_init(hwpm_linux);
|
|
||||||
|
|
||||||
ret = tegra_hwpm_get_chip_info(hwpm_linux);
|
ret = tegra_hwpm_get_chip_info(hwpm_linux);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
tegra_hwpm_err(hwpm, "Failed to get current chip info");
|
tegra_hwpm_err(hwpm, "Failed to get current chip info");
|
||||||
@@ -186,6 +179,15 @@ static int tegra_hwpm_probe(struct platform_device *pdev)
|
|||||||
goto init_sw_components_fail;
|
goto init_sw_components_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hwpm->active_chip->clk_rst_prepare) {
|
||||||
|
ret = hwpm->active_chip->clk_rst_prepare(hwpm_linux);
|
||||||
|
if (ret != 0) {
|
||||||
|
goto clock_reset_fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tegra_hwpm_debugfs_init(hwpm_linux);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Currently VDK doesn't have a fmodel for SOC HWPM. Therefore, we
|
* Currently VDK doesn't have a fmodel for SOC HWPM. Therefore, we
|
||||||
* enable fake registers on VDK for minimal testing.
|
* enable fake registers on VDK for minimal testing.
|
||||||
@@ -202,10 +204,11 @@ static int tegra_hwpm_probe(struct platform_device *pdev)
|
|||||||
tegra_hwpm_dbg(hwpm, hwpm_info, "Probe successful!");
|
tegra_hwpm_dbg(hwpm, hwpm_info, "Probe successful!");
|
||||||
goto success;
|
goto success;
|
||||||
|
|
||||||
chip_info_fail:
|
|
||||||
init_sw_components_fail:
|
|
||||||
tegra_hwpm_clk_rst_release(hwpm_linux);
|
|
||||||
clock_reset_fail:
|
clock_reset_fail:
|
||||||
|
tegra_hwpm_release_ip_register_node(hwpm);
|
||||||
|
tegra_hwpm_release_sw_setup(hwpm);
|
||||||
|
init_sw_components_fail:
|
||||||
|
chip_info_fail:
|
||||||
device_destroy(&hwpm_linux->class, hwpm_linux->dev_t);
|
device_destroy(&hwpm_linux->class, hwpm_linux->dev_t);
|
||||||
device_create:
|
device_create:
|
||||||
cdev_del(&hwpm_linux->cdev);
|
cdev_del(&hwpm_linux->cdev);
|
||||||
@@ -243,7 +246,9 @@ static int tegra_hwpm_remove(struct platform_device *pdev)
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
tegra_hwpm_clk_rst_release(hwpm_linux);
|
if (hwpm->active_chip->clk_rst_release) {
|
||||||
|
hwpm->active_chip->clk_rst_release(hwpm_linux);
|
||||||
|
}
|
||||||
|
|
||||||
tegra_hwpm_release_ip_register_node(hwpm);
|
tegra_hwpm_release_ip_register_node(hwpm);
|
||||||
tegra_hwpm_release_sw_setup(hwpm);
|
tegra_hwpm_release_sw_setup(hwpm);
|
||||||
|
|||||||
@@ -401,9 +401,11 @@ static int tegra_hwpm_open(struct inode *inode, struct file *filp)
|
|||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = tegra_hwpm_clk_rst_set_rate_enable(hwpm_linux);
|
if (hwpm->active_chip->clk_rst_set_rate_enable) {
|
||||||
if (ret != 0) {
|
ret = tegra_hwpm_clk_rst_set_rate_enable(hwpm_linux);
|
||||||
goto fail;
|
if (ret != 0) {
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = tegra_hwpm_setup_hw(hwpm);
|
ret = tegra_hwpm_setup_hw(hwpm);
|
||||||
@@ -477,14 +479,14 @@ static int tegra_hwpm_release(struct inode *inode, struct file *filp)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = tegra_hwpm_disable_triggers(hwpm);
|
ret = tegra_hwpm_disable_triggers(hwpm);
|
||||||
if (ret < 0) {
|
if (ret != 0) {
|
||||||
tegra_hwpm_err(hwpm, "Failed to disable PMA triggers");
|
tegra_hwpm_err(hwpm, "Failed to disable PMA triggers");
|
||||||
err = ret;
|
err = ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disable and release reserved IPs */
|
/* Disable and release reserved IPs */
|
||||||
ret = tegra_hwpm_release_resources(hwpm);
|
ret = tegra_hwpm_release_resources(hwpm);
|
||||||
if (ret < 0) {
|
if (ret != 0) {
|
||||||
tegra_hwpm_err(hwpm, "Failed to release IP apertures");
|
tegra_hwpm_err(hwpm, "Failed to release IP apertures");
|
||||||
err = ret;
|
err = ret;
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -492,7 +494,7 @@ static int tegra_hwpm_release(struct inode *inode, struct file *filp)
|
|||||||
|
|
||||||
/* Clear MEM_BYTES pipeline */
|
/* Clear MEM_BYTES pipeline */
|
||||||
ret = tegra_hwpm_clear_mem_pipeline(hwpm);
|
ret = tegra_hwpm_clear_mem_pipeline(hwpm);
|
||||||
if (ret < 0) {
|
if (ret != 0) {
|
||||||
tegra_hwpm_err(hwpm, "Failed to clear MEM_BYTES pipeline");
|
tegra_hwpm_err(hwpm, "Failed to clear MEM_BYTES pipeline");
|
||||||
err = ret;
|
err = ret;
|
||||||
goto fail;
|
goto fail;
|
||||||
@@ -502,17 +504,19 @@ static int tegra_hwpm_release(struct inode *inode, struct file *filp)
|
|||||||
tegra_hwpm_release_mem_mgmt(hwpm);
|
tegra_hwpm_release_mem_mgmt(hwpm);
|
||||||
|
|
||||||
ret = tegra_hwpm_release_hw(hwpm);
|
ret = tegra_hwpm_release_hw(hwpm);
|
||||||
if (ret < 0) {
|
if (ret != 0) {
|
||||||
tegra_hwpm_err(hwpm, "Failed to release hw");
|
tegra_hwpm_err(hwpm, "Failed to release hw");
|
||||||
err = ret;
|
err = ret;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = tegra_hwpm_clk_rst_disable(hwpm_linux);
|
if (hwpm->active_chip->clk_rst_disable) {
|
||||||
if (ret < 0) {
|
ret = tegra_hwpm_clk_rst_disable(hwpm_linux);
|
||||||
tegra_hwpm_err(hwpm, "Failed to release clock");
|
if (ret != 0) {
|
||||||
err = ret;
|
tegra_hwpm_err(hwpm, "Failed to release clock");
|
||||||
goto fail;
|
err = ret;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* De-init driver on last close call only */
|
/* De-init driver on last close call only */
|
||||||
|
|||||||
@@ -36,19 +36,16 @@ static struct hwpm_soc_chip_info chip_info = {
|
|||||||
};
|
};
|
||||||
static bool chip_info_initialized;
|
static bool chip_info_initialized;
|
||||||
|
|
||||||
#if (!defined(CONFIG_ACPI))
|
|
||||||
const struct hwpm_soc_chip_info t234_chip_info = {
|
const struct hwpm_soc_chip_info t234_chip_info = {
|
||||||
.chip_id = 0x23,
|
.chip_id = 0x23,
|
||||||
.chip_id_rev = 0x4,
|
.chip_id_rev = 0x4,
|
||||||
.platform = PLAT_SI,
|
.platform = PLAT_SI,
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
|
||||||
/* This function should be invoked only once before retrieving soc chip info */
|
/* This function should be invoked only once before retrieving soc chip info */
|
||||||
int tegra_hwpm_init_chip_info(struct tegra_hwpm_os_linux *hwpm_linux)
|
int tegra_hwpm_init_chip_info(struct tegra_hwpm_os_linux *hwpm_linux)
|
||||||
{
|
{
|
||||||
struct device *dev = hwpm_linux->dev;
|
struct device *dev = hwpm_linux->dev;
|
||||||
struct tegra_soc_hwpm *hwpm = &hwpm_linux->hwpm;
|
|
||||||
#if defined(CONFIG_ACPI)
|
#if defined(CONFIG_ACPI)
|
||||||
const struct acpi_device_id *id;
|
const struct acpi_device_id *id;
|
||||||
#endif
|
#endif
|
||||||
@@ -56,19 +53,19 @@ int tegra_hwpm_init_chip_info(struct tegra_hwpm_os_linux *hwpm_linux)
|
|||||||
if (chip_info_initialized) {
|
if (chip_info_initialized) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(CONFIG_ACPI)
|
#if defined(CONFIG_ACPI)
|
||||||
|
/* Get device node info from ACPI table */
|
||||||
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
id = acpi_match_device(dev->driver->acpi_match_table, dev);
|
||||||
if (!id) {
|
if (id) {
|
||||||
tegra_hwpm_err(hwpm, "Couldn't find matching ACPI device");
|
chip_info.chip_id = (id->driver_data >> 8) & 0xff;
|
||||||
return -ENODEV;
|
chip_info.chip_id_rev = (id->driver_data >> 4) & 0xf;
|
||||||
|
chip_info.platform = (id->driver_data >> 20) & 0xf;
|
||||||
|
|
||||||
|
goto complete;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
chip_info.chip_id = (id->driver_data >> 8) & 0xff;
|
/* Get device node info from device tree */
|
||||||
chip_info.chip_id_rev = (id->driver_data >> 4) & 0xf;
|
|
||||||
chip_info.platform = (id->driver_data >> 20) & 0xf;
|
|
||||||
|
|
||||||
goto complete;
|
|
||||||
#else /* !CONFIG_ACPI */
|
|
||||||
if (of_machine_is_compatible("nvidia,tegra234")) {
|
if (of_machine_is_compatible("nvidia,tegra234")) {
|
||||||
chip_info.chip_id = t234_chip_info.chip_id;
|
chip_info.chip_id = t234_chip_info.chip_id;
|
||||||
chip_info.chip_id_rev = t234_chip_info.chip_id_rev;
|
chip_info.chip_id_rev = t234_chip_info.chip_id_rev;
|
||||||
@@ -87,8 +84,7 @@ int tegra_hwpm_init_chip_info(struct tegra_hwpm_os_linux *hwpm_linux)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* CONFIG_ACPI */
|
return -ENODEV;
|
||||||
return -EINVAL;
|
|
||||||
complete:
|
complete:
|
||||||
chip_info_initialized = true;
|
chip_info_initialized = true;
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user