lineage_kernel_xcoverpro/drivers/samsung/debug/sec_debug_hw_param.c

657 lines
16 KiB
C
Executable File

/*
*sec_hw_param.c
*
* Copyright (c) 2016 Samsung Electronics Co., Ltd
* http://www.samsung.com
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/sec_debug.h>
#include <linux/sec_ext.h>
#include <linux/sec_class.h>
#include <linux/uaccess.h>
#include <linux/soc/samsung/exynos-soc.h>
#include <soc/samsung/exynos-pm.h>
#include <linux/io.h>
#include <linux/thermal.h>
#define MAX_DDR_VENDOR 16
#define LPDDR_BASE 0x0204C000
#define DATA_SIZE 1024
#define LOT_STRING_LEN 5
/*
* LPDDR4 (JESD209-4) MR5 Manufacturer ID
* 0000 0000B : Reserved
* 0000 0001B : Samsung
* 0000 0101B : Nanya
* 0000 0110B : SK hynix
* 0000 1000B : Winbond
* 0000 1001B : ESMT
* 1111 1111B : Micron
* All others : Reserved
*/
static char *lpddr4_manufacture_name[MAX_DDR_VENDOR] = {
"NA",
"SEC", /* Samsung */
"NA",
"NA",
"NA",
"NAN", /* Nanya */
"HYN", /* SK hynix */
"NA",
"WIN", /* Winbond */
"ESM", /* ESMT */
"NA",
"NA",
"NA",
"NA",
"NA",
"MIC", /* Micron */
};
static unsigned int sec_hw_rev;
static unsigned int chipid_fail_cnt;
static unsigned int lpi_timeout_cnt;
static unsigned int cache_err_cnt;
static unsigned int codediff_cnt;
#if defined(CONFIG_SAMSUNG_VST_CAL)
static unsigned int vst_result;
#endif
static unsigned long pcb_offset;
static unsigned long smd_offset;
static unsigned int lpddr4_size;
static char warranty = 'D';
#if 1 /* DDR training result structure */
#define MK_DDR_TRN_DATA_BASE 0x0204C000
#define NUM_OF_CH (2)
#define NUM_OF_TRN_OFFSET_INFO (2)
#define NUM_OF_TRN_DLL_INFO (1)
#define NUM_OF_TRN_GATE_INFO (4)
#define NUM_OF_TRN_RD_DESKEW_INFO (9)
#define NUM_OF_TRN_RD_DESKEWQ_INFO (2)
#define NUM_OF_TRN_WR_DESKEW_INFO (9)
#define NUM_OF_TRN_INFO (0)
enum phy_rank_info {
PHY_RANK_0,
PHY_RANK_1,
PHY_RANK_ALL,
};
enum phy_byte_info {
PHY_BYTE_0,
PHY_BYTE_1,
PHY_BYTE_ALL,
};
struct phy_trn_cbt_info_t {
unsigned char ca[6];
unsigned char ck;
unsigned char cs[2];
unsigned char cke[2];
};
struct phy_trn_read_dqs_info_t {
unsigned char center[PHY_BYTE_ALL];
unsigned char left[PHY_BYTE_ALL];
};
struct phy_trn_wr_lvl_info_t {
unsigned short code[PHY_BYTE_ALL];
};
struct phy_trn_gate_info_t {
unsigned short center[PHY_BYTE_ALL];
unsigned char cycle[PHY_BYTE_ALL];
};
struct phy_trn_read_info_t {
unsigned short deskewl[NUM_OF_TRN_RD_DESKEW_INFO][PHY_BYTE_ALL];
unsigned short deskewc[NUM_OF_TRN_RD_DESKEW_INFO][PHY_BYTE_ALL];
struct phy_trn_read_dqs_info_t dqs;
};
struct phy_trn_write_info_t {
unsigned short deskewl[NUM_OF_TRN_WR_DESKEW_INFO][PHY_BYTE_ALL];
unsigned short deskewc[NUM_OF_TRN_WR_DESKEW_INFO][PHY_BYTE_ALL];
};
struct phy_trn_all_level_deskew_offset_info_t {
signed char offset[PHY_BYTE_ALL];
};
struct phy_trn_prbs_info_t {
short read[PHY_RANK_ALL][PHY_BYTE_ALL];
short write[PHY_RANK_ALL][PHY_BYTE_ALL];
};
struct phy_trn_soc_vref_info_t {
unsigned char net_lv;
unsigned char vref;
unsigned int average;
unsigned char left;
unsigned char right;
};
union mr14_t {
volatile unsigned int data;
struct {
volatile unsigned int vref_dq : (5 - 0 + 1);
volatile unsigned int vr_dq : (6 - 6 + 1);
volatile unsigned int reserved_7 : (7 - 7 + 1);
} bitfield;
};
struct phy_trn_memory_vref_info_t {
unsigned char net_lv;
unsigned char vref;
unsigned int average;
unsigned char left;
unsigned char right;
union mr14_t mr14;
};
struct phy_trn_clock_duty_info_t {
unsigned char nmos;
unsigned char pmos;
unsigned int max_size;
};
struct phy_trn_data_t {
unsigned int dll;
struct phy_trn_cbt_info_t cbt;
struct phy_trn_wr_lvl_info_t wr_lvl;
struct phy_trn_gate_info_t gate[PHY_RANK_ALL];
struct phy_trn_read_info_t read[PHY_RANK_ALL];//read training per rank enabled from KC
struct phy_trn_write_info_t write[PHY_RANK_ALL];
struct phy_trn_all_level_deskew_offset_info_t read_offset[11][PHY_RANK_ALL];
struct phy_trn_all_level_deskew_offset_info_t write_offset[11][PHY_RANK_ALL];
struct phy_trn_prbs_info_t prbs;
struct phy_trn_soc_vref_info_t soc_vref[3][PHY_RANK_ALL];
struct phy_trn_memory_vref_info_t memory_vref[2][PHY_RANK_ALL];
struct phy_trn_clock_duty_info_t clock_duty;
};
#endif /* DDR training result structure */
static int __init sec_hw_param_get_hw_rev(char *arg)
{
get_option(&arg, &sec_hw_rev);
return 0;
}
early_param("androidboot.revision", sec_hw_param_get_hw_rev);
static int __init sec_hw_param_check_chip_id(char *arg)
{
get_option(&arg, &chipid_fail_cnt);
return 0;
}
early_param("sec_debug.chipidfail_cnt", sec_hw_param_check_chip_id);
static int __init sec_hw_param_check_lpi_timeout(char *arg)
{
get_option(&arg, &lpi_timeout_cnt);
return 0;
}
early_param("sec_debug.lpitimeout_cnt", sec_hw_param_check_lpi_timeout);
static int __init sec_hw_param_cache_error(char *arg)
{
get_option(&arg, &cache_err_cnt);
return 0;
}
early_param("sec_debug.cache_err_cnt", sec_hw_param_cache_error);
static int __init sec_hw_param_code_diff(char *arg)
{
get_option(&arg, &codediff_cnt);
return 0;
}
early_param("sec_debug.codediff_cnt", sec_hw_param_code_diff);
#if defined(CONFIG_SAMSUNG_VST_CAL)
static int __init sec_hw_param_vst_result(char *arg)
{
get_option(&arg, &vst_result);
return 0;
}
early_param("sec_debug.vst_result", sec_hw_param_vst_result);
#endif
static int __init sec_hw_param_pcb_offset(char *arg)
{
pcb_offset = simple_strtoul(arg, NULL, 10);
return 0;
}
early_param("sec_debug.pcb_offset", sec_hw_param_pcb_offset);
static int __init sec_hw_param_smd_offset(char *arg)
{
smd_offset = simple_strtoul(arg, NULL, 10);
return 0;
}
early_param("sec_debug.smd_offset", sec_hw_param_smd_offset);
static int __init sec_hw_param_lpddr4_size(char *arg)
{
get_option(&arg, &lpddr4_size);
return 0;
}
early_param("sec_debug.lpddr4_size", sec_hw_param_lpddr4_size);
static int __init sec_hw_param_bin(char *arg)
{
warranty = (char)*arg;
return 0;
}
early_param("sec_debug.bin", sec_hw_param_bin);
static u32 chipid_reverse_value(u32 value, u32 bitcnt)
{
int tmp, ret = 0;
int i;
for (i = 0; i < bitcnt; i++) {
tmp = (value >> i) & 0x1;
ret += tmp << ((bitcnt - 1) - i);
}
return ret;
}
static void chipid_dec_to_36(u32 in, char *p)
{
int mod;
int i;
for (i = LOT_STRING_LEN - 1; i >= 1; i--) {
mod = in % 36;
in /= 36;
p[i] = (mod < 10) ? (mod + '0') : (mod - 10 + 'A');
}
p[0] = 'N';
p[LOT_STRING_LEN] = '\0';
}
static char *get_dram_manufacturer(void)
{
void *lpddr_reg;
u64 val;
int mr5_vendor_id = 0;
lpddr_reg = ioremap(LPDDR_BASE, SZ_64);
if (!lpddr_reg) {
pr_err("failed to get i/o address lpddr_reg\n");
return lpddr4_manufacture_name[mr5_vendor_id];
}
val = readq((void __iomem *)lpddr_reg);
mr5_vendor_id = (val & 0xff00) >> 8;
//for micron
if (mr5_vendor_id == 0xFF)
mr5_vendor_id = 0xF;
else if (mr5_vendor_id >= MAX_DDR_VENDOR) {
pr_err("others : manufacture %d \n", mr5_vendor_id);
mr5_vendor_id = 0;
}
return lpddr4_manufacture_name[mr5_vendor_id];
}
static ssize_t sec_hw_param_ap_info_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
ssize_t info_size = 0;
int reverse_id_0 = 0;
u32 tmp = 0;
char lot_id[LOT_STRING_LEN + 1];
reverse_id_0 = chipid_reverse_value(exynos_soc_info.lot_id, 32);
tmp = (reverse_id_0 >> 11) & 0x1FFFFF;
chipid_dec_to_36(tmp, lot_id);
info_size += snprintf(buf, DATA_SIZE, "\"HW_REV\":\"%d\",", sec_hw_rev);
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"BIN\":\"%c\",", warranty);
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"ASB\":\"%d\",", id_get_asb_ver());
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"PSITE\":\"%d\",", id_get_product_line());
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"LOT_ID\":\"%s\",", lot_id);
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"CHIPID_FAIL\":\"%d\",", chipid_fail_cnt);
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"LPI_TIMEOUT\":\"%d\",", lpi_timeout_cnt);
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"CACHE_ERR\":\"%d\",", cache_err_cnt);
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"CODE_DIFF\":\"%d\",", codediff_cnt);
#if defined(CONFIG_SAMSUNG_VST_CAL)
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"VST_RESULT\":\"%d\",", vst_result);
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"VST_ADJUST\":\"%d\",", volt_vst_cal_bdata);
#endif
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"ASV_BIG\":\"%d\",", asv_ids_information(bg));
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"ASV_LIT\":\"%d\",", asv_ids_information(lg));
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"ASV_MIF\":\"%d\",", asv_ids_information(mifg));
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"IDS_BIG\":\"%d\"", asv_ids_information(bids));
return info_size;
}
static ssize_t sec_hw_param_ddr_info_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
ssize_t info_size = 0;
struct phy_trn_data_t *trn_data;
unsigned long addr;
unsigned int data;
int i, j, k;
unsigned short min_vwm, temp_vwm;
void __iomem *dram_trn_addr;
info_size +=
snprintf((char *)(buf), DATA_SIZE, "\"DDRV\":\"%s\",",
get_dram_manufacturer());
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"LPDDR4\":\"%dGB\",", lpddr4_size);
dram_trn_addr = ioremap(MK_DDR_TRN_DATA_BASE, 0x4000);
for (k = 0; k < NUM_OF_CH; k++) {
data = readl(dram_trn_addr + 0x60 + 4 * k);
info_size +=
snprintf(
(char *)(buf + info_size),
DATA_SIZE - info_size,
"\"sc%dr0\":\"%x\",", k, (data >> 16) & 0xff);
info_size +=
snprintf(
(char *)(buf + info_size),
DATA_SIZE - info_size,
"\"sc%dr1\":\"%x\",", k, data & 0xff);
}
for (k = 0; k < NUM_OF_CH; k++) {
data = readl(dram_trn_addr + 0x80 + 4 * k);
info_size +=
snprintf(
(char *)(buf + info_size),
DATA_SIZE - info_size,
"\"dc%dr0\":\"%x\",", k, (data >> 16) & 0xff);
info_size +=
snprintf(
(char *)(buf + info_size),
DATA_SIZE - info_size,
"\"dc%dr1\":\"%x\",", k, data & 0xff);
}
for (k = 0; k < NUM_OF_CH; k++) {
addr = readl(dram_trn_addr + 0x50 + 4 * k);
if (addr > 0x13000 && addr < 0x15000) {
trn_data =
(struct phy_trn_data_t *)
(dram_trn_addr + addr - 0x13000);
for (j = 0; j < PHY_BYTE_ALL; j++) {
for (i = 0; i < NUM_OF_TRN_RD_DESKEW_INFO; i++) {
if (i == 0) {
min_vwm = 2 *
(trn_data->read[0].deskewc[i][j] -
trn_data->read[0].deskewl[i][j]);
} else {
temp_vwm = 2 *
(trn_data->read[0].deskewc[i][j] -
trn_data->read[0].deskewl[i][j]);
if (min_vwm > temp_vwm)
min_vwm = temp_vwm;
}
}
info_size +=
snprintf(
(char *)(buf + info_size),
DATA_SIZE - info_size,
"\"rc%db%d\":\"%x\",", k, j, min_vwm);
}
for (j = 0; j < PHY_BYTE_ALL; j++) {
for (i = 0; i < NUM_OF_TRN_WR_DESKEW_INFO; i++) {
if (i == 0) {
min_vwm = 2 *
(trn_data->write[0].deskewc[i][j] -
trn_data->write[0].deskewl[i][j]);
} else {
temp_vwm = 2 *
(trn_data->write[0].deskewc[i][j] -
trn_data->write[0].deskewl[i][j]);
if (min_vwm > temp_vwm)
min_vwm = temp_vwm;
}
}
info_size +=
snprintf(
(char *)(buf + info_size),
DATA_SIZE - info_size,
"\"wc%db%d\":\"%x\",", k, j, min_vwm);
}
}
}
iounmap(dram_trn_addr);
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"C2D\":\"\",");
info_size +=
snprintf((char *)(buf + info_size), DATA_SIZE - info_size,
"\"D2D\":\"\"");
return info_size;
}
static ssize_t sec_hw_param_extra_info_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
ssize_t info_size = 0;
sec_debug_store_extra_info_A(buf);
info_size = strlen(buf);
return info_size;
}
static ssize_t sec_hw_param_extrb_info_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
ssize_t info_size = 0;
sec_debug_store_extra_info_B(buf);
info_size = strlen(buf);
return info_size;
}
static ssize_t sec_hw_param_extrc_info_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
ssize_t info_size = 0;
sec_debug_store_extra_info_C(buf);
info_size = strlen(buf);
return info_size;
}
static ssize_t sec_hw_param_extrm_info_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
ssize_t info_size = 0;
sec_debug_store_extra_info_M(buf);
info_size = strlen(buf);
return info_size;
}
static ssize_t sec_hw_param_extrf_info_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
ssize_t info_size = 0;
sec_debug_store_extra_info_F(buf);
info_size = strlen(buf);
return info_size;
}
static ssize_t sec_hw_param_pcb_info_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
unsigned char barcode[6] = {0,};
int ret = -1;
strncpy(barcode, buf, 5);
ret = sec_set_param_str(pcb_offset, barcode, 5);
if (ret < 0)
pr_err("%s : Set Param fail. offset (%lu), data (%s)", __func__, pcb_offset, barcode);
return count;
}
static ssize_t sec_hw_param_smd_info_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
unsigned char smd_date[9] = {0,};
int ret = -1;
strncpy(smd_date, buf, 8);
ret = sec_set_param_str(smd_offset, smd_date, 8);
if (ret < 0)
pr_err("%s : Set Param fail. offset (%lu), data (%s)", __func__, smd_offset, smd_date);
return count;
}
static ssize_t sec_hw_param_thermal_info_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
ssize_t info_size = 0;
return info_size;
}
static struct kobj_attribute sec_hw_param_ap_info_attr =
__ATTR(ap_info, 0440, sec_hw_param_ap_info_show, NULL);
static struct kobj_attribute sec_hw_param_ddr_info_attr =
__ATTR(ddr_info, 0440, sec_hw_param_ddr_info_show, NULL);
static struct kobj_attribute sec_hw_param_extra_info_attr =
__ATTR(extra_info, 0440, sec_hw_param_extra_info_show, NULL);
static struct kobj_attribute sec_hw_param_extrb_info_attr =
__ATTR(extrb_info, 0440, sec_hw_param_extrb_info_show, NULL);
static struct kobj_attribute sec_hw_param_extrc_info_attr =
__ATTR(extrc_info, 0440, sec_hw_param_extrc_info_show, NULL);
static struct kobj_attribute sec_hw_param_extrm_info_attr =
__ATTR(extrm_info, 0440, sec_hw_param_extrm_info_show, NULL);
static struct kobj_attribute sec_hw_param_extrf_info_attr =
__ATTR(extrf_info, 0440, sec_hw_param_extrf_info_show, NULL);
static struct kobj_attribute sec_hw_param_pcb_info_attr =
__ATTR(pcb_info, 0660, NULL, sec_hw_param_pcb_info_store);
static struct kobj_attribute sec_hw_param_smd_info_attr =
__ATTR(smd_info, 0660, NULL, sec_hw_param_smd_info_store);
static struct kobj_attribute sec_hw_param_thermal_info_attr =
__ATTR(thermal_info, 0440, sec_hw_param_thermal_info_show, NULL);
static struct attribute *sec_hw_param_attributes[] = {
&sec_hw_param_ap_info_attr.attr,
&sec_hw_param_ddr_info_attr.attr,
&sec_hw_param_extra_info_attr.attr,
&sec_hw_param_extrb_info_attr.attr,
&sec_hw_param_extrc_info_attr.attr,
&sec_hw_param_extrm_info_attr.attr,
&sec_hw_param_extrf_info_attr.attr,
&sec_hw_param_pcb_info_attr.attr,
&sec_hw_param_smd_info_attr.attr,
&sec_hw_param_thermal_info_attr.attr,
NULL,
};
static struct attribute_group sec_hw_param_attr_group = {
.attrs = sec_hw_param_attributes,
};
static int __init sec_hw_param_init(void)
{
int ret = 0;
struct device *dev;
dev = sec_device_create(NULL, "sec_hw_param");
ret = sysfs_create_group(&dev->kobj, &sec_hw_param_attr_group);
if (ret)
pr_err("%s : could not create sysfs noden", __func__);
return 0;
}
device_initcall(sec_hw_param_init);