[PATCH 1/3] soc: mediatek: mtk-socinfo: Add driver for getting chip information

From: William-tw Lin
Date: Tue Jul 18 2023 - 07:22:02 EST


Add driver for socinfo retrieval. This patch includes the following:
1. mtk-socinfo driver for chip info retrieval
2. Related changes to Makefile and Kconfig

Signed-off-by: William-tw Lin <william-tw.lin@xxxxxxxxxxxx>
---
drivers/soc/mediatek/Kconfig | 18 +++
drivers/soc/mediatek/Makefile | 1 +
drivers/soc/mediatek/mtk-socinfo.c | 203 +++++++++++++++++++++++++++
drivers/soc/mediatek/mtk-socinfo.h | 213 +++++++++++++++++++++++++++++
4 files changed, 435 insertions(+)
create mode 100644 drivers/soc/mediatek/mtk-socinfo.c
create mode 100644 drivers/soc/mediatek/mtk-socinfo.h

diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig
index a88cf04fc803..50bd9ec75ca5 100644
--- a/drivers/soc/mediatek/Kconfig
+++ b/drivers/soc/mediatek/Kconfig
@@ -91,4 +91,22 @@ config MTK_SVS
chip process corner, temperatures and other factors. Then DVFS
driver could apply SVS bank voltage to PMIC/Buck.

+config MTK_SOCINFO
+ tristate "MediaTek SOCINFO"
+ depends on MTK_EFUSE && NVMEM
+ help
+ Say y here to enable mtk socinfo information.
+ This enables a sysfs node which shows attributes of MTK soc info.
+ Information of soc info includes the manufacturer, the marketing
+ name, and the soc used.
+
+config MTK_SOCINFO_DEBUG
+ tristate "MediaTek SOCINFO debug"
+ depends on MTK_SOCINFO
+ help
+ Say y here to enables a debug node which shows MTK SOC information.
+ This enables a debug node that gives details on MTK soc info attributes.
+ Information included in this debug node includes the manufacturer, the marketing
+ name, the soc used, as well as the segment of the soc.
+
endmenu
diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile
index 8c0ddacbcde8..c900e5f13a35 100644
--- a/drivers/soc/mediatek/Makefile
+++ b/drivers/soc/mediatek/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_MTK_SCPSYS_PM_DOMAINS) += mtk-pm-domains.o
obj-$(CONFIG_MTK_MMSYS) += mtk-mmsys.o
obj-$(CONFIG_MTK_MMSYS) += mtk-mutex.o
obj-$(CONFIG_MTK_SVS) += mtk-svs.o
+obj-$(CONFIG_MTK_SOCINFO) += mtk-socinfo.o
diff --git a/drivers/soc/mediatek/mtk-socinfo.c b/drivers/soc/mediatek/mtk-socinfo.c
new file mode 100644
index 000000000000..8c8964eb4670
--- /dev/null
+++ b/drivers/soc/mediatek/mtk-socinfo.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/device.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
+#include <linux/sys_soc.h>
+#include <linux/slab.h>
+#include "mtk-socinfo.h"
+
+
+#if IS_ENABLED(CONFIG_MTK_SOCINFO_DEBUG)
+static int mtk_socinfo_socinfo_debug_show(struct seq_file *m, void *p)
+{
+ struct mtk_socinfo *mtk_socinfop = (struct mtk_socinfo *)m->private;
+
+ seq_printf(m, "SOC Manufacturer: %s\n", soc_manufacturer);
+ seq_printf(m, "SOC Name: %s\n", mtk_socinfop->name_data->soc_name);
+ seq_printf(m, "SOC segment Name: %s\n", mtk_socinfop->name_data->soc_segment_name);
+ seq_printf(m, "Marketing Name: %s\n", mtk_socinfop->name_data->marketing_name);
+
+ return 0;
+}
+DEBUG_FOPS_RO(socinfo);
+
+static int mtk_socinfo_create_debug_cmds(struct mtk_socinfo *mtk_socinfop)
+{
+ const char *d = "mtk-socinfo";
+
+ struct mtk_socinfo_dentry {
+ const char *name;
+ const struct file_operations *fops;
+ };
+
+ struct mtk_socinfo_dentry mtk_socinfo_entries[] = {
+ MTK_SOCINFO_DENTRY_DATA(socinfo),
+ };
+
+ mtk_socinfo_dir = debugfs_create_dir(d, NULL);
+ if (IS_ERR(mtk_socinfo_dir)) {
+ dev_err(mtk_socinfop->dev, "Cannot create %s\n", d);
+ return 0;
+ }
+
+ file_entry = debugfs_create_file(mtk_socinfo_entries[0].name, 0664,
+ mtk_socinfo_dir, mtk_socinfop,
+ mtk_socinfo_entries[0].fops);
+ if (IS_ERR(file_entry)) {
+ dev_err(mtk_socinfop->dev, "Cannot create %s/%s\n", d, mtk_socinfo_entries[0].name);
+ return PTR_ERR(file_entry);
+ }
+ return 0;
+}
+#endif
+
+static int mtk_socinfo_create_socinfo_node(struct mtk_socinfo *mtk_socinfop)
+{
+ struct soc_device_attribute *attrs;
+ static char machine[30] = {0};
+
+ attrs = devm_kzalloc(mtk_socinfop->dev, sizeof(*attrs), GFP_KERNEL);
+ if (!attrs) {
+ return -ENOMEM;
+ }
+
+ snprintf(machine, 30, "%s (%s)", mtk_socinfop->name_data->marketing_name,
+ mtk_socinfop->name_data->soc_name);
+ attrs->family = soc_manufacturer;
+ attrs->machine = machine;
+
+ soc_dev = soc_device_register(attrs);
+ if (IS_ERR(soc_dev)) {
+ dev_err(mtk_socinfop->dev, "Cannot create soc node, soc_device_register failed\n");
+ return PTR_ERR(soc_dev);
+ }
+
+ dev_info(mtk_socinfop->dev, "%s %s SoC detected.\n", MODULE_NAME, attrs->machine);
+ return 0;
+}
+
+static int mtk_socinfo_get_socinfo_data(struct mtk_socinfo *mtk_socinfop)
+{
+ struct efuse_data *soc_efuse_data_infop = mtk_socinfop->soc_data->efuse_data;
+ struct name_data *soc_name_data_infop = mtk_socinfop->soc_data->name_data;
+ unsigned int soc_efuse_data_count = mtk_socinfop->soc_data->efuse_data_count;
+ unsigned int soc_segment_count = mtk_socinfop->soc_data->efuse_segment_count;
+ unsigned int i = 0, j = 0;
+ struct efuse_data *temp_efuse_datap;
+ struct nvmem_cell *cell;
+ size_t efuse_bytes;
+ char *nvmem_cell_name = "";
+ u32 *efuse_temp_bufp;
+ int match_segment_index = -1;
+
+ for (i = 0; i < soc_segment_count; i++) {
+ bool match_segment = true;
+
+ for (j = 0; j < soc_efuse_data_count; j++) {
+ temp_efuse_datap = soc_efuse_data_infop + (i * soc_efuse_data_count + j);
+ nvmem_cell_name = temp_efuse_datap->nvmem_cell_name;
+ cell = nvmem_cell_get(mtk_socinfop->dev, nvmem_cell_name);
+ if (IS_ERR_OR_NULL(cell)) {
+ dev_err(mtk_socinfop->dev, "%s cell not found\n", nvmem_cell_name);
+ goto out;
+ }
+ efuse_temp_bufp = (u32 *)nvmem_cell_read(cell, &efuse_bytes);
+ nvmem_cell_put(cell);
+ if (*efuse_temp_bufp != temp_efuse_datap->efuse_data) {
+ match_segment = false;
+ break;
+ }
+ }
+ if (match_segment) {
+ match_segment_index = i;
+ mtk_socinfop->name_data = soc_name_data_infop + i;
+ }
+ }
+
+out:
+ kfree(efuse_temp_bufp);
+ return match_segment_index;
+}
+
+static const struct of_device_id mtk_socinfo_id_table[] = {
+ { .compatible = "mediatek,mt8173-socinfo", .data = &socinfo_data_table[INDEX_MT8173]},
+ { .compatible = "mediatek,mt8183-socinfo", .data = &socinfo_data_table[INDEX_MT8183]},
+ { .compatible = "mediatek,mt8186-socinfo", .data = &socinfo_data_table[INDEX_MT8186]},
+ { .compatible = "mediatek,mt8188-socinfo", .data = &socinfo_data_table[INDEX_MT8188]},
+ { .compatible = "mediatek,mt8192-socinfo", .data = &socinfo_data_table[INDEX_MT8192]},
+ { .compatible = "mediatek,mt8195-socinfo", .data = &socinfo_data_table[INDEX_MT8195]},
+ { },
+};
+MODULE_DEVICE_TABLE(of, mtk_socinfo_id_table);
+
+static int mtk_socinfo_probe(struct platform_device *pdev)
+{
+ struct mtk_socinfo *mtk_socinfop;
+ int ret = 0;
+
+ mtk_socinfop = devm_kzalloc(&pdev->dev, sizeof(*mtk_socinfop), GFP_KERNEL);
+ if (!mtk_socinfop)
+ return -ENOMEM;
+
+ mtk_socinfop->dev = &pdev->dev;
+ mtk_socinfop->soc_data = (struct socinfo_data *)of_device_get_match_data(mtk_socinfop->dev);
+ if (!mtk_socinfop->soc_data) {
+ dev_err(mtk_socinfop->dev, "No mtk-socinfo platform data found\n");
+ return -EPERM;
+ }
+
+ ret = mtk_socinfo_get_socinfo_data(mtk_socinfop);
+ if (ret < 0) {
+ dev_err(mtk_socinfop->dev, "Failed to get socinfo data (ret = %d)\n", ret);
+ return -EINVAL;
+ }
+
+ ret = mtk_socinfo_create_socinfo_node(mtk_socinfop);
+ if (ret != 0) {
+ dev_err(mtk_socinfop->dev, "Failed to create socinfo node (ret = %d)\n", ret);
+ return ret;
+ }
+
+#if IS_ENABLED(CONFIG_MTK_SOCINFO_DEBUG)
+ ret = mtk_socinfo_create_debug_cmds(mtk_socinfop);
+ if (ret != 0) {
+ dev_err(mtk_socinfop->dev, "Failed to create socinfo debug node (ret = %d)\n", ret);
+ return ret;
+ }
+#endif
+ return 0;
+}
+
+static int mtk_socinfo_remove(struct platform_device *pdev)
+{
+ if (soc_dev)
+ soc_device_unregister(soc_dev);
+#if IS_ENABLED(CONFIG_MTK_SOCINFO_DEBUG)
+ debugfs_remove(file_entry);
+#endif
+ return 0;
+}
+
+static struct platform_driver mtk_socinfo = {
+ .probe = mtk_socinfo_probe,
+ .remove = mtk_socinfo_remove,
+ .driver = {
+ .name = "mtk_socinfo",
+ .owner = THIS_MODULE,
+ .of_match_table = mtk_socinfo_id_table,
+ },
+};
+module_platform_driver(mtk_socinfo);
+MODULE_AUTHOR("William-TW LIN <william-tw.lin@xxxxxxxxxxxx>");
+MODULE_DESCRIPTION("Mediatek socinfo driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/soc/mediatek/mtk-socinfo.h b/drivers/soc/mediatek/mtk-socinfo.h
new file mode 100644
index 000000000000..8fd490311c8b
--- /dev/null
+++ b/drivers/soc/mediatek/mtk-socinfo.h
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (c) 2023 MediaTek Inc.
+ */
+
+#ifndef __MTK_SOCINFO_H__
+#define __MTK_SOCINFO_H__
+
+#define MODULE_NAME "[mtk-socinfo]"
+
+#define DEBUG_FOPS_RO(name) \
+ static int mtk_socinfo_##name##_debug_open(struct inode *inode, \
+ struct file *filp) \
+ { \
+ return single_open(filp, mtk_socinfo_##name##_debug_show, \
+ inode->i_private); \
+ } \
+ static const struct file_operations mtk_socinfo_##name##_debug_fops = { \
+ .owner = THIS_MODULE, \
+ .open = mtk_socinfo_##name##_debug_open, \
+ .read = seq_read, \
+ .llseek = seq_lseek, \
+ .release = single_release, \
+ }
+
+#define MTK_SOCINFO_DENTRY_DATA(name) {__stringify(name), &mtk_socinfo_##name##_debug_fops}
+
+const char *soc_manufacturer = "MediaTek";
+
+struct soc_device *soc_dev;
+struct dentry *mtk_socinfo_dir, *file_entry;
+
+struct mtk_socinfo {
+ struct device *dev;
+ struct name_data *name_data;
+ struct socinfo_data *soc_data;
+};
+
+struct efuse_data {
+ char *nvmem_cell_name;
+ u32 efuse_data;
+};
+
+struct name_data {
+ char *soc_name;
+ char *soc_segment_name;
+ char *marketing_name;
+};
+
+struct socinfo_data {
+ char *soc_name;
+ struct efuse_data *efuse_data;
+ struct name_data *name_data;
+ unsigned int efuse_segment_count;
+ unsigned int efuse_data_count;
+};
+
+enum socinfo_data_index {
+ INDEX_MT8186 = 0,
+ INDEX_MT8188,
+ INDEX_MT8195,
+ INDEX_MT8192,
+ INDEX_MT8183,
+ INDEX_MT8173,
+ SOCINFO_DATA_LAST_INDEX
+};
+
+/* begin 8186 info */
+#define mtk_mt8186_EFUSE_DATA_COUNT 1
+static struct efuse_data mtk_mt8186_efuse_data_info[][mtk_mt8186_EFUSE_DATA_COUNT] = {
+ {{.nvmem_cell_name = "socinfo-data1", .efuse_data = 0x81861001}},
+ {{.nvmem_cell_name = "socinfo-data1", .efuse_data = 0x81862001}},
+};
+
+static struct name_data mtk_mt8186_name_data_info[] = {
+ {.soc_name = "MT8186",
+ .soc_segment_name = "MT8186GV/AZA",
+ .marketing_name = "Kompanio 520"},
+ {.soc_name = "MT8186T",
+ .soc_segment_name = "MT8186TV/AZA",
+ .marketing_name = "Kompanio 528"},
+};
+/* end 8186 info */
+
+/* begin 8188 info */
+#define mtk_mt8188_EFUSE_DATA_COUNT 2
+static struct efuse_data mtk_mt8188_efuse_data_info[][mtk_mt8188_EFUSE_DATA_COUNT] = {
+ {{.nvmem_cell_name = "socinfo-data1", .efuse_data = 0x81880000},
+ {.nvmem_cell_name = "socinfo-data2", .efuse_data = 0x00000010}},
+ {{.nvmem_cell_name = "socinfo-data1", .efuse_data = 0x81880000},
+ {.nvmem_cell_name = "socinfo-data2", .efuse_data = 0x00000011}},
+};
+
+static struct name_data mtk_mt8188_name_data_info[] = {
+ {.soc_name = "MT8188",
+ .soc_segment_name = "MT8188GV/AZA",
+ .marketing_name = "Kompanio 830"},
+ {.soc_name = "MT8188",
+ .soc_segment_name = "MT8188GV/HZA",
+ .marketing_name = "Kompanio 830"},
+};
+/* end 8188 info */
+
+/* begin 8195 info */
+#define mtk_mt8195_EFUSE_DATA_COUNT 1
+static struct efuse_data mtk_mt8195_efuse_data_info[][mtk_mt8195_EFUSE_DATA_COUNT] = {
+ {{.nvmem_cell_name = "socinfo-data1", .efuse_data = 0x81950300}},
+ {{.nvmem_cell_name = "socinfo-data1", .efuse_data = 0x81950304}},
+ {{.nvmem_cell_name = "socinfo-data1", .efuse_data = 0x81950400}},
+ {{.nvmem_cell_name = "socinfo-data1", .efuse_data = 0x81950404}},
+};
+
+static struct name_data mtk_mt8195_name_data_info[] = {
+ {.soc_name = "MT8195",
+ .soc_segment_name = "MT8195GV/EZA",
+ .marketing_name = "Kompanio 1200"},
+ {.soc_name = "MT8195",
+ .soc_segment_name = "MT8195GV/EHZA",
+ .marketing_name = "Kompanio 1200"},
+ {.soc_name = "MT8195T",
+ .soc_segment_name = "MT8195TV/EZA",
+ .marketing_name = "Kompanio 1380"},
+ {.soc_name = "MT8195T",
+ .soc_segment_name = "MT8195TV/EHZA",
+ .marketing_name = "Kompanio 1380"},
+};
+/* end 8195 info */
+
+/* begin 8192 info */
+#define mtk_mt8192_EFUSE_DATA_COUNT 2
+static struct efuse_data mtk_mt8192_efuse_data_info[][mtk_mt8192_EFUSE_DATA_COUNT] = {
+ {{.nvmem_cell_name = "socinfo-data1", .efuse_data = 0x00001100},
+ {.nvmem_cell_name = "socinfo-data2", .efuse_data = 0x00040080}},
+ {{.nvmem_cell_name = "socinfo-data1", .efuse_data = 0x00000100},
+ {.nvmem_cell_name = "socinfo-data2", .efuse_data = 0x000400C0}},
+};
+
+static struct name_data mtk_mt8192_name_data_info[] = {
+ {.soc_name = "MT8192",
+ .soc_segment_name = "MT8192V/AZA",
+ .marketing_name = "Kompanio 820"},
+ {.soc_name = "MT8192T",
+ .soc_segment_name = "MT8192V/ATZA",
+ .marketing_name = "Kompanio 828"},
+};
+/* end 8192 info */
+
+/* begin 8183 info */
+#define mtk_mt8183_EFUSE_DATA_COUNT 2
+static struct efuse_data mtk_mt8183_efuse_data_info[][mtk_mt8183_EFUSE_DATA_COUNT] = {
+ {{.nvmem_cell_name = "socinfo-data1", .efuse_data = 0x00010043},
+ {.nvmem_cell_name = "socinfo-data2", .efuse_data = 0x00000840}},
+};
+
+static struct name_data mtk_mt8183_name_data_info[] = {
+ {.soc_name = "MT8183",
+ .soc_segment_name = "MT8183V/AZA",
+ .marketing_name = "Kompanio 500"},
+};
+/* end 8183 info */
+
+/* begin 8173 info */
+#define mtk_mt8173_EFUSE_DATA_COUNT 2
+static struct efuse_data mtk_mt8173_efuse_data_info[][mtk_mt8173_EFUSE_DATA_COUNT] = {
+ {{.nvmem_cell_name = "socinfo-data1", .efuse_data = 0x6CA20004},
+ {.nvmem_cell_name = "socinfo-data2", .efuse_data = 0x10000000}},
+};
+
+static struct name_data mtk_mt8173_name_data_info[] = {
+ {.soc_name = "MT8173",
+ .soc_segment_name = "MT8173V/AC",
+ .marketing_name = "MT8173"},
+};
+/* end 8173 info */
+
+/* begin socinfo_data table */
+/* for get_soc_data lookup purposes */
+static struct socinfo_data socinfo_data_table[] = {
+ [INDEX_MT8186] = {.soc_name = "mt8186",
+ .efuse_data = &(mtk_mt8186_efuse_data_info[0][0]),
+ .name_data = mtk_mt8186_name_data_info,
+ .efuse_segment_count = ARRAY_SIZE(mtk_mt8186_name_data_info),
+ .efuse_data_count = mtk_mt8186_EFUSE_DATA_COUNT},
+ [INDEX_MT8188] = {.soc_name = "mt8188",
+ .efuse_data = &(mtk_mt8188_efuse_data_info[0][0]),
+ .name_data = mtk_mt8188_name_data_info,
+ .efuse_segment_count = ARRAY_SIZE(mtk_mt8188_name_data_info),
+ .efuse_data_count = mtk_mt8188_EFUSE_DATA_COUNT},
+ [INDEX_MT8195] = {.soc_name = "mt8195",
+ .efuse_data = &(mtk_mt8195_efuse_data_info[0][0]),
+ .name_data = mtk_mt8195_name_data_info,
+ .efuse_segment_count = ARRAY_SIZE(mtk_mt8195_name_data_info),
+ .efuse_data_count = mtk_mt8195_EFUSE_DATA_COUNT},
+ [INDEX_MT8192] = {.soc_name = "mt8192",
+ .efuse_data = &(mtk_mt8192_efuse_data_info[0][0]),
+ .name_data = mtk_mt8192_name_data_info,
+ .efuse_segment_count = ARRAY_SIZE(mtk_mt8192_name_data_info),
+ .efuse_data_count = mtk_mt8192_EFUSE_DATA_COUNT},
+ [INDEX_MT8183] = {.soc_name = "mt8183",
+ .efuse_data = &(mtk_mt8183_efuse_data_info[0][0]),
+ .name_data = mtk_mt8183_name_data_info,
+ .efuse_segment_count = ARRAY_SIZE(mtk_mt8183_name_data_info),
+ .efuse_data_count = mtk_mt8183_EFUSE_DATA_COUNT},
+ [INDEX_MT8173] = {.soc_name = "mt8173",
+ .efuse_data = &(mtk_mt8173_efuse_data_info[0][0]),
+ .name_data = mtk_mt8173_name_data_info,
+ .efuse_segment_count = ARRAY_SIZE(mtk_mt8173_name_data_info),
+ .efuse_data_count = mtk_mt8173_EFUSE_DATA_COUNT},
+};
+/* end socinfo_data table */
+
+
+#endif /* __MTK_SOCINFO_H__ */
--
2.18.0