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