18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2019 Samsung Electronics Co., Ltd.
48c2ecf20Sopenharmony_ci *	      http://www.samsung.com/
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Exynos - CHIP ID support
78c2ecf20Sopenharmony_ci * Author: Pankaj Dubey <pankaj.dubey@samsung.com>
88c2ecf20Sopenharmony_ci * Author: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
138c2ecf20Sopenharmony_ci#include <linux/of.h>
148c2ecf20Sopenharmony_ci#include <linux/regmap.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/soc/samsung/exynos-chipid.h>
178c2ecf20Sopenharmony_ci#include <linux/sys_soc.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic const struct exynos_soc_id {
208c2ecf20Sopenharmony_ci	const char *name;
218c2ecf20Sopenharmony_ci	unsigned int id;
228c2ecf20Sopenharmony_ci} soc_ids[] = {
238c2ecf20Sopenharmony_ci	{ "EXYNOS3250", 0xE3472000 },
248c2ecf20Sopenharmony_ci	{ "EXYNOS4210", 0x43200000 },	/* EVT0 revision */
258c2ecf20Sopenharmony_ci	{ "EXYNOS4210", 0x43210000 },
268c2ecf20Sopenharmony_ci	{ "EXYNOS4212", 0x43220000 },
278c2ecf20Sopenharmony_ci	{ "EXYNOS4412", 0xE4412000 },
288c2ecf20Sopenharmony_ci	{ "EXYNOS5250", 0x43520000 },
298c2ecf20Sopenharmony_ci	{ "EXYNOS5260", 0xE5260000 },
308c2ecf20Sopenharmony_ci	{ "EXYNOS5410", 0xE5410000 },
318c2ecf20Sopenharmony_ci	{ "EXYNOS5420", 0xE5420000 },
328c2ecf20Sopenharmony_ci	{ "EXYNOS5440", 0xE5440000 },
338c2ecf20Sopenharmony_ci	{ "EXYNOS5800", 0xE5422000 },
348c2ecf20Sopenharmony_ci	{ "EXYNOS7420", 0xE7420000 },
358c2ecf20Sopenharmony_ci	{ "EXYNOS5433", 0xE5433000 },
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic const char * __init product_id_to_soc_id(unsigned int product_id)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	int i;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(soc_ids); i++)
438c2ecf20Sopenharmony_ci		if ((product_id & EXYNOS_MASK) == soc_ids[i].id)
448c2ecf20Sopenharmony_ci			return soc_ids[i].name;
458c2ecf20Sopenharmony_ci	return NULL;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic int __init exynos_chipid_early_init(void)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct soc_device_attribute *soc_dev_attr;
518c2ecf20Sopenharmony_ci	struct soc_device *soc_dev;
528c2ecf20Sopenharmony_ci	struct device_node *root;
538c2ecf20Sopenharmony_ci	struct device_node *syscon;
548c2ecf20Sopenharmony_ci	struct regmap *regmap;
558c2ecf20Sopenharmony_ci	u32 product_id;
568c2ecf20Sopenharmony_ci	u32 revision;
578c2ecf20Sopenharmony_ci	int ret;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	syscon = of_find_compatible_node(NULL, NULL,
608c2ecf20Sopenharmony_ci					 "samsung,exynos4210-chipid");
618c2ecf20Sopenharmony_ci	if (!syscon)
628c2ecf20Sopenharmony_ci		return -ENODEV;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	regmap = device_node_to_regmap(syscon);
658c2ecf20Sopenharmony_ci	of_node_put(syscon);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (IS_ERR(regmap))
688c2ecf20Sopenharmony_ci		return PTR_ERR(regmap);
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	ret = regmap_read(regmap, EXYNOS_CHIPID_REG_PRO_ID, &product_id);
718c2ecf20Sopenharmony_ci	if (ret < 0)
728c2ecf20Sopenharmony_ci		return ret;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	revision = product_id & EXYNOS_REV_MASK;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
778c2ecf20Sopenharmony_ci	if (!soc_dev_attr)
788c2ecf20Sopenharmony_ci		return -ENOMEM;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	soc_dev_attr->family = "Samsung Exynos";
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	root = of_find_node_by_path("/");
838c2ecf20Sopenharmony_ci	of_property_read_string(root, "model", &soc_dev_attr->machine);
848c2ecf20Sopenharmony_ci	of_node_put(root);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%x", revision);
878c2ecf20Sopenharmony_ci	soc_dev_attr->soc_id = product_id_to_soc_id(product_id);
888c2ecf20Sopenharmony_ci	if (!soc_dev_attr->soc_id) {
898c2ecf20Sopenharmony_ci		pr_err("Unknown SoC\n");
908c2ecf20Sopenharmony_ci		ret = -ENODEV;
918c2ecf20Sopenharmony_ci		goto err;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* please note that the actual registration will be deferred */
958c2ecf20Sopenharmony_ci	soc_dev = soc_device_register(soc_dev_attr);
968c2ecf20Sopenharmony_ci	if (IS_ERR(soc_dev)) {
978c2ecf20Sopenharmony_ci		ret = PTR_ERR(soc_dev);
988c2ecf20Sopenharmony_ci		goto err;
998c2ecf20Sopenharmony_ci	}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	/* it is too early to use dev_info() here (soc_dev is NULL) */
1028c2ecf20Sopenharmony_ci	pr_info("soc soc0: Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n",
1038c2ecf20Sopenharmony_ci		soc_dev_attr->soc_id, product_id, revision);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	return 0;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cierr:
1088c2ecf20Sopenharmony_ci	kfree(soc_dev_attr->revision);
1098c2ecf20Sopenharmony_ci	kfree(soc_dev_attr);
1108c2ecf20Sopenharmony_ci	return ret;
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ciearly_initcall(exynos_chipid_early_init);
114