mirror of
git://nv-tegra.nvidia.com/linux-nvgpu.git
synced 2025-12-24 10:34:43 +03:00
Current clk unit has multiple header files under pmuif folder. This has combination of public struct which is accessed outside the unit and private struct which is accessed within clk unit. This patch segregates them based on their accessibility. All private items are moved into ucode_clk_inf.h from pmuif which only clk can access. All public items are moved into include/clk.h which other units can access This will help in documentation of items for public items. NVGPU-4491 Change-Id: Iccb0571e05ecb3cb13363390bed8c7214409b543 Signed-off-by: Abdul Salam <absalam@nvidia.com> Reviewed-on: https://git-master.nvidia.com/r/c/linux-nvgpu/+/2292318 Tested-by: mobile promotions <svcmobile_promotions@nvidia.com> Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
348 lines
10 KiB
C
348 lines
10 KiB
C
/*
|
|
* Copyright (c) 2018-2020, NVIDIA CORPORATION. All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
* DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include <unit/unit.h>
|
|
#include <unit/io.h>
|
|
#include <nvgpu/posix/io.h>
|
|
#include <nvgpu/enabled.h>
|
|
#include <nvgpu/gk20a.h>
|
|
#include <nvgpu/fuse.h>
|
|
#include <nvgpu/hal_init.h>
|
|
#include <nvgpu/pmu/clk/clk.h>
|
|
|
|
#include "hal/fuse/fuse_gm20b.h"
|
|
|
|
#include "nvgpu-fuse-priv.h"
|
|
#include "nvgpu-fuse-tu104.h"
|
|
|
|
|
|
/* register definitions for this block */
|
|
#define TU104_FUSE_REG_BASE 0x00021000U
|
|
#define TU104_FUSE_STATUS_OPT_PRIV_SEC_EN (TU104_FUSE_REG_BASE+0x434U)
|
|
#define TU104_FUSE_OPT_ADC_CAL_FUSE_REV (TU104_FUSE_REG_BASE+0x64CU)
|
|
#define TU104_FUSE_OPT_ADC_CAL_GPC0 (TU104_FUSE_REG_BASE+0x650U)
|
|
#define TU104_FUSE_OPT_ADC_CAL_GPC1_DELTA (TU104_FUSE_REG_BASE+0x654U)
|
|
#define TU104_FUSE_OPT_ADC_CAL_GPC2_DELTA (TU104_FUSE_REG_BASE+0x658U)
|
|
#define TU104_FUSE_OPT_ADC_CAL_GPC3_DELTA (TU104_FUSE_REG_BASE+0x65CU)
|
|
#define TU104_FUSE_OPT_ADC_CAL_GPC4_DELTA (TU104_FUSE_REG_BASE+0x660U)
|
|
#define TU104_FUSE_OPT_ADC_CAL_GPC5_DELTA (TU104_FUSE_REG_BASE+0x664U)
|
|
#define TU104_FUSE_OPT_ADC_CAL_SHARED_DELTA (TU104_FUSE_REG_BASE+0x668U)
|
|
|
|
|
|
/* for common init args */
|
|
struct fuse_test_args tu104_init_args = {
|
|
.gpu_arch = 0x16,
|
|
.gpu_impl = 0x4,
|
|
.fuse_base_addr = TU104_FUSE_REG_BASE,
|
|
.sec_fuse_addr = TU104_FUSE_STATUS_OPT_PRIV_SEC_EN,
|
|
};
|
|
|
|
/* Verify fuse API to read cal fuse revision */
|
|
int test_fuse_tu104_vin_cal_rev(struct unit_module *m,
|
|
struct gk20a *g, void *__args)
|
|
{
|
|
const u32 rev = 0x3;
|
|
u32 val;
|
|
int ret = UNIT_SUCCESS;
|
|
|
|
nvgpu_posix_io_writel_reg_space(g, TU104_FUSE_OPT_ADC_CAL_FUSE_REV,
|
|
rev);
|
|
|
|
val = g->ops.fuse.read_vin_cal_fuse_rev(g);
|
|
|
|
if (val != rev) {
|
|
unit_err(m, "%s: cal fuse rev invalid 0x%x != 0x%x\n",
|
|
__func__, val, rev);
|
|
ret = UNIT_FAIL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* common function used for calculating calibration value from fuse */
|
|
static u32 calculate_cal_unsigned(u32 fuse_val, u8 int_start, u8 int_bits,
|
|
u8 frac_start, u8 frac_bits)
|
|
{
|
|
u32 int_mask = (1 << int_bits) - 1;
|
|
u32 frac_mask = (1 << frac_bits) - 1;
|
|
u32 tmp;
|
|
|
|
tmp = ((fuse_val >> int_start) & int_mask) << frac_bits;
|
|
tmp += (fuse_val >> frac_start) & frac_mask;
|
|
tmp = (tmp * 1000) >> frac_bits;
|
|
|
|
return tmp;
|
|
}
|
|
|
|
/* calculate slope value from GPC0 fuse value */
|
|
static u32 gpc0_expected_slope(u32 gpc0_fuse, u32 this_fuse)
|
|
{
|
|
return calculate_cal_unsigned(this_fuse, 10, 4, 0, 10);
|
|
}
|
|
|
|
/* calculate intercept value from GPC0 fuse value */
|
|
static u32 gpc0_expected_intercept(u32 gpc0_fuse, u32 this_fuse)
|
|
{
|
|
return calculate_cal_unsigned(this_fuse, 16, 12, 14, 2);
|
|
}
|
|
|
|
/* calculate slope value from GPC0 and delta values (GPC1-5,etc) fuse value */
|
|
static u32 gpc1_expected_slope(u32 gpc0_fuse, u32 gpc1_fuse)
|
|
{
|
|
u32 gpc0_slope = gpc0_expected_slope(gpc0_fuse, gpc0_fuse);
|
|
u32 gpc1_delta = ((gpc1_fuse >> 10) & 0x1) * 1000;
|
|
u32 gpc1_delta_positive = ((gpc1_fuse >> 11) & 0x1) == 0;
|
|
|
|
if (gpc1_delta_positive) {
|
|
return gpc0_slope + gpc1_delta;
|
|
} else {
|
|
return gpc0_slope - gpc1_delta;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* calculate intercept value from GPC0 and delta values (GPC1-5,etc) fuse
|
|
* value
|
|
*/
|
|
static u32 gpc1_expected_intercept(u32 gpc0_fuse, u32 gpc1_fuse)
|
|
{
|
|
u32 gpc0_intercept = gpc0_expected_intercept(gpc0_fuse, gpc0_fuse);
|
|
u32 gpc1_delta = calculate_cal_unsigned(gpc1_fuse, 14, 8, 12, 2);
|
|
u32 gpc1_delta_positive = ((gpc1_fuse >> 22) & 0x1) == 0;
|
|
|
|
if (gpc1_delta_positive) {
|
|
return gpc0_intercept + gpc1_delta;
|
|
} else {
|
|
return gpc0_intercept - gpc1_delta;
|
|
}
|
|
}
|
|
|
|
static s8 fuse_expected_gain(u32 this_fuse)
|
|
{
|
|
return (s8)((this_fuse >> 16U) & 0x1fU);
|
|
}
|
|
|
|
static s8 fuse_expected_offset(u32 this_fuse)
|
|
{
|
|
return (s8)(this_fuse & 0x7fU);
|
|
}
|
|
|
|
/*
|
|
* Verify fuse API to read cal fuse revision
|
|
* Loops through table of fuse values and expected results
|
|
* Validates invalid data checks
|
|
*/
|
|
int test_fuse_tu104_vin_cal_slope_intercept(struct unit_module *m,
|
|
struct gk20a *g, void *__args)
|
|
{
|
|
int result;
|
|
u32 slope, intercept;
|
|
s8 gain, offset;
|
|
int ret = UNIT_SUCCESS;
|
|
|
|
/* table for storing fuse values and expected results */
|
|
struct vin_test_struct {
|
|
u32 vin_id;
|
|
u32 fuse_addr;
|
|
u32 gpc0_fuse_val;
|
|
u32 fuse_val;
|
|
u32 (*expected_slope)(u32 gpc0_fuse, u32 this_fuse);
|
|
u32 (*expected_intercept)(u32 gpc0_fuse, u32 this_fuse);
|
|
};
|
|
struct vin_test_struct vin_test_table[] = {
|
|
{
|
|
CTRL_CLK_VIN_ID_GPC0, TU104_FUSE_OPT_ADC_CAL_GPC0,
|
|
0x00214421, 0x00214421,
|
|
gpc0_expected_slope, gpc0_expected_intercept,
|
|
},
|
|
{
|
|
CTRL_CLK_VIN_ID_GPC1, TU104_FUSE_OPT_ADC_CAL_GPC1_DELTA,
|
|
0x00214421, 0x00214421,
|
|
gpc1_expected_slope, gpc1_expected_intercept,
|
|
},
|
|
{
|
|
CTRL_CLK_VIN_ID_GPC2, TU104_FUSE_OPT_ADC_CAL_GPC2_DELTA,
|
|
0x00000000, 0x00614c21,
|
|
gpc1_expected_slope, gpc1_expected_intercept,
|
|
},
|
|
{
|
|
CTRL_CLK_VIN_ID_GPC3, TU104_FUSE_OPT_ADC_CAL_GPC3_DELTA,
|
|
0x00214421, 0xaaaaaaaa,
|
|
gpc1_expected_slope, gpc1_expected_intercept,
|
|
},
|
|
{
|
|
CTRL_CLK_VIN_ID_GPC4, TU104_FUSE_OPT_ADC_CAL_GPC4_DELTA,
|
|
0x00214421, 0x55555555,
|
|
gpc1_expected_slope, gpc1_expected_intercept,
|
|
},
|
|
{
|
|
CTRL_CLK_VIN_ID_GPC5, TU104_FUSE_OPT_ADC_CAL_GPC5_DELTA,
|
|
0x00214421, 0xefffffff,
|
|
gpc1_expected_slope, gpc1_expected_intercept,
|
|
},
|
|
{
|
|
CTRL_CLK_VIN_ID_SYS, TU104_FUSE_OPT_ADC_CAL_SHARED_DELTA,
|
|
0x00214421, 0xfffffffe,
|
|
gpc1_expected_slope, gpc1_expected_intercept,
|
|
},
|
|
{
|
|
CTRL_CLK_VIN_ID_XBAR, TU104_FUSE_OPT_ADC_CAL_SHARED_DELTA,
|
|
0x00214421, 0x11111111,
|
|
gpc1_expected_slope, gpc1_expected_intercept,
|
|
},
|
|
{
|
|
CTRL_CLK_VIN_ID_LTC, TU104_FUSE_OPT_ADC_CAL_SHARED_DELTA,
|
|
0x00214421, 0x00000001,
|
|
gpc1_expected_slope, gpc1_expected_intercept,
|
|
},
|
|
};
|
|
int vin_table_len = sizeof(vin_test_table)/sizeof(vin_test_table[0]);
|
|
int i;
|
|
|
|
for (i = 0; i < vin_table_len; i++) {
|
|
u32 gpc0_fuse_val = vin_test_table[i].gpc0_fuse_val;
|
|
u32 this_fuse_val = vin_test_table[i].fuse_val;
|
|
u32 expected_slope, expected_intercept;
|
|
s8 expected_gain, expected_offset;
|
|
|
|
nvgpu_posix_io_writel_reg_space(g, TU104_FUSE_OPT_ADC_CAL_GPC0,
|
|
gpc0_fuse_val);
|
|
nvgpu_posix_io_writel_reg_space(g,
|
|
vin_test_table[i].fuse_addr,
|
|
this_fuse_val);
|
|
|
|
result = g->ops.fuse.read_vin_cal_slope_intercept_fuse(g,
|
|
vin_test_table[i].vin_id,
|
|
&slope, &intercept);
|
|
if (result != 0) {
|
|
unit_err(m, "%s: read_vin_cal_slope_intercept_fuse "
|
|
" returned error %d, i = %d\n",
|
|
__func__, result, i);
|
|
ret = UNIT_FAIL;
|
|
}
|
|
|
|
expected_slope = vin_test_table[i].expected_slope(
|
|
gpc0_fuse_val,
|
|
this_fuse_val);
|
|
expected_intercept = vin_test_table[i].expected_intercept(
|
|
gpc0_fuse_val,
|
|
this_fuse_val);
|
|
if (slope != expected_slope) {
|
|
unit_err(m, "%s: read_vin_cal_slope_intercept_fuse "
|
|
" reported bad slope 0x%x != 0x%x, i=%d\n",
|
|
__func__, slope, expected_slope, i);
|
|
ret = UNIT_FAIL;
|
|
}
|
|
if (intercept != expected_intercept) {
|
|
unit_err(m, "%s: read_vin_cal_slope_intercept_fuse "
|
|
" reported bad intercept 0x%x != 0x%x, i=%d\n",
|
|
__func__, intercept, expected_intercept, i);
|
|
ret = UNIT_FAIL;
|
|
}
|
|
|
|
result = g->ops.fuse.read_vin_cal_gain_offset_fuse(g,
|
|
vin_test_table[i].vin_id,
|
|
&gain, &offset);
|
|
if (result != 0) {
|
|
unit_err(m, "%s: read_vin_cal_gain_offset_fuse "
|
|
" returned error %d, i = %d\n",
|
|
__func__, result, i);
|
|
ret = UNIT_FAIL;
|
|
}
|
|
|
|
expected_gain = fuse_expected_gain(this_fuse_val);
|
|
if (gain != expected_gain) {
|
|
unit_err(m, "%s: read_vin_cal_gain_offset_fuse "
|
|
" reported bad gain 0x%x != 0x%x, i=%d\n",
|
|
__func__, gain, expected_gain, i);
|
|
ret = UNIT_FAIL;
|
|
}
|
|
|
|
expected_offset = fuse_expected_offset(this_fuse_val);
|
|
if (offset != expected_offset) {
|
|
unit_err(m, "%s: read_vin_cal_gain_offset_fuse "
|
|
" reported bad offset 0x%x != 0x%x, i=%d\n",
|
|
__func__, offset, expected_offset, i);
|
|
ret = UNIT_FAIL;
|
|
}
|
|
}
|
|
|
|
/* test invalid GPC0 data special case */
|
|
nvgpu_posix_io_writel_reg_space(g, TU104_FUSE_OPT_ADC_CAL_GPC0,
|
|
~0U);
|
|
result = g->ops.fuse.read_vin_cal_slope_intercept_fuse(g,
|
|
CTRL_CLK_VIN_ID_GPC0,
|
|
&slope, &intercept);
|
|
if (result == 0) {
|
|
unit_err(m, "%s: read_vin_cal_slope_intercept_fuse did NOT "
|
|
" return error for bad GPC0 data\n", __func__);
|
|
ret = UNIT_FAIL;
|
|
}
|
|
result = g->ops.fuse.read_vin_cal_gain_offset_fuse(g,
|
|
CTRL_CLK_VIN_ID_GPC0,
|
|
&gain, &offset);
|
|
if (result == 0) {
|
|
unit_err(m, "%s: read_vin_cal_gain_offset_fuse did NOT "
|
|
" return error for bad GPC0 data\n", __func__);
|
|
ret = UNIT_FAIL;
|
|
}
|
|
/* restore valid data */
|
|
nvgpu_posix_io_writel_reg_space(g, TU104_FUSE_OPT_ADC_CAL_GPC0,
|
|
0U);
|
|
|
|
/* test invalid GPC1 data for the bad delta data case */
|
|
nvgpu_posix_io_writel_reg_space(g, TU104_FUSE_OPT_ADC_CAL_GPC1_DELTA,
|
|
~0U);
|
|
result = g->ops.fuse.read_vin_cal_slope_intercept_fuse(g,
|
|
CTRL_CLK_VIN_ID_GPC1,
|
|
&slope, &intercept);
|
|
if (result == 0) {
|
|
unit_err(m, "%s: read_vin_cal_slope_intercept_fuse did NOT "
|
|
" return error for bad GPC1 value\n", __func__);
|
|
ret = UNIT_FAIL;
|
|
}
|
|
/* restore valid data */
|
|
nvgpu_posix_io_writel_reg_space(g, TU104_FUSE_OPT_ADC_CAL_GPC1_DELTA,
|
|
0U);
|
|
/* test invalid VIN ID */
|
|
result = g->ops.fuse.read_vin_cal_slope_intercept_fuse(g,
|
|
~0U,
|
|
&slope,
|
|
&intercept);
|
|
if (result == 0) {
|
|
unit_err(m, "%s: read_vin_cal_slope_intercept_fuse did NOT "
|
|
" return error for invalid VIN ID\n", __func__);
|
|
ret = UNIT_FAIL;
|
|
}
|
|
|
|
/* test API with invalid VIN id */
|
|
result = g->ops.fuse.read_vin_cal_gain_offset_fuse(g, ~0U,
|
|
&gain, &offset);
|
|
if (result == 0) {
|
|
unit_err(m, "%s: read_vin_cal_gain_offset_fuse did NOT "
|
|
" return error for invalid VIN id\n", __func__);
|
|
ret = UNIT_FAIL;
|
|
}
|
|
|
|
return ret;
|
|
}
|