blob: 63b5e96ee4741481c9303b42e8494d4735b27739 [file] [edit]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2019 Google LLC
* Author: Hyunki Koo <[email protected]>
*/
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/bitrev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/sys_soc.h>
#include <linux/module.h>
struct gs_chipid_variant {
int product_ver;
int unique_id_reg;
int main_rev_reg;
int sub_rev_reg;
int pkg_rev_reg;
int main_rev_bit;
int sub_rev_bit;
int pkg_rev_bit;
int dvfs_version_reg;
};
#define RAW_HEX_STR_SIZE 172
#define AP_HW_TUNE_HEX_STR_SIZE 64
#define AP_HW_TUNE_HEX_ARRAY_SIZE 32
#define ASV_TBL_HEX_STR_SIZE 128
#define HPM_ASV_HEX_STR_SIZE 128
#define GS101_HPM_ASV_END_ADDR 0xA024
#define GS201_HPM_ASV_END_ADDR 0xA02C
#define ZUMA_HPM_ASV_END_ADDR 0xA040
#define ZUMAPRO_HPM_ASV_END_ADDR 0xA03C
static void gs_chipid_get_asv_tbl_str(void __iomem *reg);
static void gs_chipid_get_hpm_asv_str(void __iomem *reg);
/**
* Struct gs_chipid_info
* @soc_product_id: product id allocated to gs SoC
* @soc_revision: revision of gs SoC
*/
struct gs_chipid_info {
bool initialized;
u32 product_id;
u32 type;
u32 revision;
u32 main_rev;
u32 sub_rev;
u32 lot_id;
char *lot_id2;
u32 dvfs_version;
u32 pkg_revision;
u64 unique_id;
char ap_hw_tune_str[AP_HW_TUNE_HEX_STR_SIZE+1];
u8 ap_hw_tune_arr[AP_HW_TUNE_HEX_ARRAY_SIZE];
char asv_tbl_str[ASV_TBL_HEX_STR_SIZE+1];
char hpm_asv_str[HPM_ASV_HEX_STR_SIZE+1];
char raw_str[RAW_HEX_STR_SIZE+1];
struct device_node *node;
struct gs_chipid_variant *drv_data;
struct platform_device *pdev;
};
#define GS101_SOC_ID 0x09845000
#define GS201_SOC_ID 0x09855000
#define ZUMA_SOC_ID 0x09865000
#define ZUMAPRO_SOC_ID 0x09875000
#define SOC_MASK 0xFFFFF000
#define SOC_MASK_V2 0x00FFFFFF
#define SOC_TYPE_MASK 0x000000FF
#define GS201_TYPE_MASK 0x00F00000
#define GS201_TYPE_SHIFT 20
#define LOTID_MASK 0x001FFFFF
#define REV_MASK 0xF
#define PKG_REV_MASK 0x3
#define MAIN_REV_1 0x10
#define MAIN_REV_2 0x20
#define SUB_REV1 0x1
#define SUB_REV2 0x2
static struct gs_chipid_info gs_soc_info;
static const char *product_id_to_name(unsigned int product_id)
{
const char *soc_name;
unsigned int soc_id = product_id;
switch (soc_id) {
case GS101_SOC_ID:
soc_name = "GS101";
break;
case GS201_SOC_ID:
soc_name = "GS201";
break;
case ZUMA_SOC_ID:
soc_name = "ZUMA";
break;
case ZUMAPRO_SOC_ID:
soc_name = "ZUMAPRO";
break;
default:
soc_name = "UNKNOWN";
}
return soc_name;
}
static const struct gs_chipid_variant drv_data_gs101 = {
.product_ver = 1,
.unique_id_reg = 0x04,
.main_rev_reg = 0x0,
.main_rev_bit = 0, /* product_id.major[3:0] */
.sub_rev_reg = 0x10,
.sub_rev_bit = 16, /* chipid_rev.minor[19:16] */
.pkg_rev_reg = 0x0,
.pkg_rev_bit = 8, /* product_id.pkg_mode[9:8] */
.dvfs_version_reg = 0x900C,
};
static const struct gs_chipid_variant drv_data_gs201 = {
.product_ver = 1,
.unique_id_reg = 0x04,
.main_rev_reg = 0x10,
.main_rev_bit = 20, /* chipid_rev.major[23:20] */
.sub_rev_reg = 0x10,
.sub_rev_bit = 16, /* chipid_rev.minor[19:16] */
.pkg_rev_reg = 0x10,
.pkg_rev_bit = 24, /* chipid_rev.pkg_mode[25:24] */
.dvfs_version_reg = 0x900C,
};
static const struct gs_chipid_variant drv_data_zuma = {
.product_ver = 1,
.unique_id_reg = 0x04,
.main_rev_reg = 0x0,
.main_rev_bit = 0, /* product_id.major[3:0] */
.sub_rev_reg = 0x10,
.sub_rev_bit = 16, /* chipid_rev.minor[19:16] */
.pkg_rev_reg = 0x10,
.pkg_rev_bit = 24, /* chipid_rev.pkg_mode[25:24] */
.dvfs_version_reg = 0x900C,
};
static char lot_id[6];
static void chipid_dec_to_36(u32 in, u32 uniq_id1, char *p)
{
u32 mod;
u32 i;
u32 val;
for (i = 4; i >= 1; i--) {
mod = in % 36;
in /= 36;
p[i] = (mod < 10) ? (mod + '0') : (mod - 10 + 'A');
}
val = (uniq_id1 >> 10) & 0x3;
switch (val) {
case 0:
p[0] = 'N';
break;
case 1:
p[0] = 'S';
break;
case 2:
p[0] = 'A';
break;
case 3:
default:
break;
}
p[5] = 0;
}
/*
* sysfs implementation for gs-snapshot
* you can access the sysfs of gs-snapshot to /sys/devices/system/chip-id
* path.
*/
static struct bus_type chipid_subsys = {
.name = "chip-id",
.dev_name = "chip-id",
};
static ssize_t product_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%08X\n", gs_soc_info.product_id);
}
static ssize_t unique_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%010llX\n", gs_soc_info.unique_id);
}
static ssize_t lot_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%08X\n", gs_soc_info.lot_id);
}
static ssize_t lot_id2_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%s\n", gs_soc_info.lot_id2);
}
static ssize_t dvfs_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%u\n", gs_soc_info.dvfs_version);
}
static ssize_t pkg_revision_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%u\n", gs_soc_info.pkg_revision);
}
static ssize_t revision_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%08X\n", gs_soc_info.revision);
}
static ssize_t evt_ver_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "EVT%1X.%1X\n",
gs_soc_info.main_rev, gs_soc_info.sub_rev);
}
static ssize_t raw_str_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%s\n", gs_soc_info.raw_str);
}
static ssize_t ap_hw_tune_str_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%s\n", gs_soc_info.ap_hw_tune_str);
}
static ssize_t asv_tbl_str_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
void __iomem *reg;
reg = of_iomap(gs_soc_info.node, 0);
gs_chipid_get_asv_tbl_str(reg);
iounmap(reg);
return scnprintf(buf, PAGE_SIZE, "%s\n", gs_soc_info.asv_tbl_str);
}
static ssize_t hpm_asv_str_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
void __iomem *reg;
reg = of_iomap(gs_soc_info.node, 0);
gs_chipid_get_hpm_asv_str(reg);
iounmap(reg);
return scnprintf(buf, PAGE_SIZE, "%s\n", gs_soc_info.hpm_asv_str);
}
static DEVICE_ATTR_RO(product_id);
static DEVICE_ATTR_RO(unique_id);
static DEVICE_ATTR_RO(lot_id);
static DEVICE_ATTR_RO(lot_id2);
static DEVICE_ATTR_RO(dvfs_version);
static DEVICE_ATTR_RO(pkg_revision);
static DEVICE_ATTR_RO(revision);
static DEVICE_ATTR_RO(evt_ver);
static DEVICE_ATTR_RO(raw_str);
static DEVICE_ATTR_RO(ap_hw_tune_str);
static DEVICE_ATTR_RO(asv_tbl_str);
static DEVICE_ATTR_RO(hpm_asv_str);
static struct attribute *chipid_sysfs_attrs[] = {
&dev_attr_product_id.attr,
&dev_attr_unique_id.attr,
&dev_attr_lot_id.attr,
&dev_attr_lot_id2.attr,
&dev_attr_dvfs_version.attr,
&dev_attr_pkg_revision.attr,
&dev_attr_revision.attr,
&dev_attr_evt_ver.attr,
&dev_attr_raw_str.attr,
&dev_attr_ap_hw_tune_str.attr,
&dev_attr_asv_tbl_str.attr,
&dev_attr_hpm_asv_str.attr,
NULL,
};
static struct attribute_group chipid_sysfs_group = {
.attrs = chipid_sysfs_attrs,
};
static const struct attribute_group *chipid_sysfs_groups[] = {
&chipid_sysfs_group,
NULL,
};
static int chipid_sysfs_init(void)
{
int ret = 0;
ret = subsys_system_register(&chipid_subsys, chipid_sysfs_groups);
if (ret)
dev_err(&gs_soc_info.pdev->dev,
"fail to register chip-id subsys\n");
return ret;
}
u32 gs_chipid_get_type(void)
{
if (!gs_soc_info.initialized)
return -EPROBE_DEFER;
return gs_soc_info.type;
}
EXPORT_SYMBOL_GPL(gs_chipid_get_type);
s32 gs_chipid_get_dvfs_version(void)
{
if (!gs_soc_info.initialized)
return -EPROBE_DEFER;
return gs_soc_info.dvfs_version;
}
EXPORT_SYMBOL_GPL(gs_chipid_get_dvfs_version);
u32 gs_chipid_get_revision(void)
{
if (!gs_soc_info.initialized)
return -EPROBE_DEFER;
return gs_soc_info.revision;
}
EXPORT_SYMBOL_GPL(gs_chipid_get_revision);
u32 gs_chipid_get_product_id(void)
{
if (!gs_soc_info.initialized)
return -EPROBE_DEFER;
return gs_soc_info.product_id;
}
EXPORT_SYMBOL_GPL(gs_chipid_get_product_id);
static void gs_chipid_get_chipid_info(void __iomem *reg)
{
const struct gs_chipid_variant *data = gs_soc_info.drv_data;
u64 val;
u32 uniq_id0, uniq_id1;
u32 temp;
val = readl_relaxed(reg);
switch (data->product_ver) {
case 2:
gs_soc_info.product_id = val & SOC_MASK_V2;
break;
case 1:
default:
gs_soc_info.product_id = val & SOC_MASK;
if (gs_soc_info.product_id == GS201_SOC_ID)
val |= (readl_relaxed(reg + data->sub_rev_reg)
& GS201_TYPE_MASK) >> GS201_TYPE_SHIFT;
gs_soc_info.type = val & SOC_TYPE_MASK;
break;
}
val = readl_relaxed(reg + data->main_rev_reg);
gs_soc_info.main_rev = (val >> data->main_rev_bit) & REV_MASK;
val = readl_relaxed(reg + data->sub_rev_reg);
gs_soc_info.sub_rev = (val >> data->sub_rev_bit) & REV_MASK;
gs_soc_info.revision = (gs_soc_info.main_rev << 4)
| gs_soc_info.sub_rev;
val = readl_relaxed(reg + data->pkg_rev_reg);
gs_soc_info.pkg_revision = (val >> data->pkg_rev_bit) & PKG_REV_MASK;
uniq_id0 = readl_relaxed(reg + data->unique_id_reg);
uniq_id1 = readl_relaxed(reg + data->unique_id_reg + 4);
val = (u64)uniq_id0 | ((u64)uniq_id1 << 32UL);
gs_soc_info.unique_id = val;
gs_soc_info.lot_id = val & LOTID_MASK;
temp = bitrev32(uniq_id0);
temp = (temp >> 11) & LOTID_MASK;
chipid_dec_to_36(temp, uniq_id1, lot_id);
gs_soc_info.lot_id2 = lot_id;
val = readb_relaxed(reg + data->dvfs_version_reg);
gs_soc_info.dvfs_version = val;
}
static void gs_chipid_get_raw_str(void __iomem *reg)
{
u32 addr;
u32 addr_end;
u8 val;
int str_pos = 0;
size_t str_buf_size = sizeof(gs_soc_info.raw_str);
for (addr = 0x4; addr < 0xA; addr++) {
val = readb_relaxed(reg + addr);
str_pos += scnprintf(gs_soc_info.raw_str + str_pos,
str_buf_size - str_pos,
"%02x", val);
}
if (gs_soc_info.product_id == GS101_SOC_ID) {
addr_end = GS101_HPM_ASV_END_ADDR;
} else if (gs_soc_info.product_id == GS201_SOC_ID) {
addr_end = GS201_HPM_ASV_END_ADDR;
} else if (gs_soc_info.product_id == ZUMA_SOC_ID) {
addr_end = ZUMA_HPM_ASV_END_ADDR;
} else {
addr_end = ZUMAPRO_HPM_ASV_END_ADDR;
}
for (addr = 0xA000; addr < addr_end; addr++) {
val = readb_relaxed(reg + addr);
str_pos += scnprintf(gs_soc_info.raw_str + str_pos,
str_buf_size - str_pos,
"%02x", val);
}
for (addr = 0x9000; addr < 0x9010; addr++) {
val = readb_relaxed(reg + addr);
str_pos += scnprintf(gs_soc_info.raw_str + str_pos,
str_buf_size - str_pos,
"%02x", val);
}
}
static void gs_chipid_get_ap_hw_tune_str(void __iomem *reg)
{
u32 addr;
u8 val;
int str_pos = 0;
int arr_pos = 0;
size_t str_buf_size = sizeof(gs_soc_info.ap_hw_tune_str);
for (addr = 0xC300; addr < (0xC300 + AP_HW_TUNE_HEX_STR_SIZE/2); addr++) {
val = readb_relaxed(reg + addr);
str_pos += scnprintf(gs_soc_info.ap_hw_tune_str + str_pos,
str_buf_size - str_pos, "%02x",
val);
if (arr_pos < ARRAY_SIZE(gs_soc_info.ap_hw_tune_arr))
gs_soc_info.ap_hw_tune_arr[arr_pos++] = val;
}
}
int gs_chipid_get_ap_hw_tune_array(const u8 **array)
{
if (!gs_soc_info.initialized)
return -EPROBE_DEFER;
*array = gs_soc_info.ap_hw_tune_arr;
return sizeof(gs_soc_info.ap_hw_tune_arr);
}
EXPORT_SYMBOL_GPL(gs_chipid_get_ap_hw_tune_array);
static void gs_chipid_get_asv_tbl_str(void __iomem *reg)
{
u32 addr;
u8 val;
int str_pos = 0;
size_t str_buf_size = sizeof(gs_soc_info.asv_tbl_str);
for (addr = 0x9000; addr < (0x9000 + ASV_TBL_HEX_STR_SIZE/2); addr++) {
val = readb_relaxed(reg + addr);
str_pos += scnprintf(gs_soc_info.asv_tbl_str + str_pos,
str_buf_size - str_pos, "%02x",
val);
}
}
static void gs_chipid_get_hpm_asv_str(void __iomem *reg)
{
u32 addr;
u8 val;
int str_pos = 0;
size_t str_buf_size = sizeof(gs_soc_info.hpm_asv_str);
for (addr = 0xA000; addr < (0xA000 + HPM_ASV_HEX_STR_SIZE/2); addr++) {
val = readb_relaxed(reg + addr);
str_pos += scnprintf(gs_soc_info.hpm_asv_str + str_pos,
str_buf_size - str_pos, "%02x",
val);
}
}
static const struct of_device_id of_gs_chipid_ids[] = {
{
.compatible = "google,gs101-chipid",
.data = &drv_data_gs101,
},
{
.compatible = "google,gs201-chipid",
.data = &drv_data_gs201,
},
{
.compatible = "google,zuma-chipid",
.data = &drv_data_zuma,
},
{},
};
MODULE_DEVICE_TABLE(of, of_gs_chipid_ids);
/**
* gs_chipid_early_init: Early chipid initialization
* @dev: pointer to chipid device
*/
void gs_chipid_early_init(void)
{
struct device_node *np;
const struct of_device_id *match;
void __iomem *reg;
if (gs_soc_info.initialized)
return;
np = of_find_matching_node_and_match(NULL, of_gs_chipid_ids, &match);
if (!np || !match)
panic("%s, failed to find chipid node or match\n", __func__);
gs_soc_info.drv_data = (struct gs_chipid_variant *)match->data;
reg = of_iomap(np, 0);
gs_soc_info.node = np;
if (!reg)
panic("%s: failed to map registers\n", __func__);
gs_chipid_get_chipid_info(reg);
gs_chipid_get_raw_str(reg);
gs_chipid_get_ap_hw_tune_str(reg);
iounmap(reg);
gs_soc_info.initialized = true;
}
static int gs_chipid_probe(struct platform_device *pdev)
{
struct soc_device_attribute *soc_dev_attr;
struct soc_device *soc_dev;
int ret;
gs_chipid_early_init();
soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
if (!soc_dev_attr)
return -ENODEV;
soc_dev_attr->family = "Diablo";
ret = of_property_read_string(of_root, "model", &soc_dev_attr->machine);
if (ret)
goto free_soc;
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%u",
gs_soc_info.revision);
if (!soc_dev_attr->revision)
goto free_soc;
soc_dev_attr->soc_id = product_id_to_name(gs_soc_info.product_id);
soc_dev = soc_device_register(soc_dev_attr);
if (IS_ERR(soc_dev))
goto free_rev;
dev_info(&pdev->dev, "CPU[%s]%s CPU_REV[0x%x] Detected\n",
product_id_to_name(gs_soc_info.product_id),
gs_soc_info.type ? "B0" : "", gs_soc_info.revision);
gs_soc_info.pdev = pdev;
chipid_sysfs_init();
return 0;
free_rev:
kfree(soc_dev_attr->revision);
free_soc:
kfree(soc_dev_attr);
return -EINVAL;
}
static struct platform_driver gs_chipid_driver = {
.driver = {
.name = "gs-chipid",
.of_match_table = of_gs_chipid_ids,
},
.probe = gs_chipid_probe,
};
module_platform_driver(gs_chipid_driver);
MODULE_DESCRIPTION("GS ChipID driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:gs-chipid");