1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2019 Samsung Electronics Co., Ltd. 4 * http://www.samsung.com/ 5 * Copyright (c) 2020 Krzysztof Kozlowski <krzk@kernel.org> 6 * 7 * Exynos - CHIP ID support 8 * Author: Pankaj Dubey <pankaj.dubey@samsung.com> 9 * Author: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> 10 * Author: Krzysztof Kozlowski <krzk@kernel.org> 11 * 12 * Samsung Exynos SoC Adaptive Supply Voltage and Chip ID support 13 */ 14 15 #include <linux/device.h> 16 #include <linux/errno.h> 17 #include <linux/mfd/syscon.h> 18 #include <linux/of.h> 19 #include <linux/platform_device.h> 20 #include <linux/regmap.h> 21 #include <linux/slab.h> 22 #include <linux/soc/samsung/exynos-chipid.h> 23 #include <linux/sys_soc.h> 24 25 #include "exynos-asv.h" 26 27 static const struct exynos_soc_id { 28 const char *name; 29 unsigned int id; 30 } soc_ids[] = { 31 /* List ordered by SoC name */ 32 { "EXYNOS3250", 0xE3472000 }, 33 { "EXYNOS4210", 0x43200000 }, /* EVT0 revision */ 34 { "EXYNOS4210", 0x43210000 }, 35 { "EXYNOS4212", 0x43220000 }, 36 { "EXYNOS4412", 0xE4412000 }, 37 { "EXYNOS5250", 0x43520000 }, 38 { "EXYNOS5260", 0xE5260000 }, 39 { "EXYNOS5410", 0xE5410000 }, 40 { "EXYNOS5420", 0xE5420000 }, 41 { "EXYNOS5433", 0xE5433000 }, 42 { "EXYNOS5440", 0xE5440000 }, 43 { "EXYNOS5800", 0xE5422000 }, 44 { "EXYNOS7420", 0xE7420000 }, 45 }; 46 47 static const char *product_id_to_soc_id(unsigned int product_id) 48 { 49 int i; 50 51 for (i = 0; i < ARRAY_SIZE(soc_ids); i++) 52 if ((product_id & EXYNOS_MASK) == soc_ids[i].id) 53 return soc_ids[i].name; 54 return NULL; 55 } 56 57 static int exynos_chipid_probe(struct platform_device *pdev) 58 { 59 struct soc_device_attribute *soc_dev_attr; 60 struct soc_device *soc_dev; 61 struct device_node *root; 62 struct regmap *regmap; 63 u32 product_id; 64 u32 revision; 65 int ret; 66 67 regmap = device_node_to_regmap(pdev->dev.of_node); 68 if (IS_ERR(regmap)) 69 return PTR_ERR(regmap); 70 71 ret = regmap_read(regmap, EXYNOS_CHIPID_REG_PRO_ID, &product_id); 72 if (ret < 0) 73 return ret; 74 75 revision = product_id & EXYNOS_REV_MASK; 76 77 soc_dev_attr = devm_kzalloc(&pdev->dev, sizeof(*soc_dev_attr), 78 GFP_KERNEL); 79 if (!soc_dev_attr) 80 return -ENOMEM; 81 82 soc_dev_attr->family = "Samsung Exynos"; 83 84 root = of_find_node_by_path("/"); 85 of_property_read_string(root, "model", &soc_dev_attr->machine); 86 of_node_put(root); 87 88 soc_dev_attr->revision = devm_kasprintf(&pdev->dev, GFP_KERNEL, 89 "%x", revision); 90 soc_dev_attr->soc_id = product_id_to_soc_id(product_id); 91 if (!soc_dev_attr->soc_id) { 92 pr_err("Unknown SoC\n"); 93 return -ENODEV; 94 } 95 96 /* please note that the actual registration will be deferred */ 97 soc_dev = soc_device_register(soc_dev_attr); 98 if (IS_ERR(soc_dev)) 99 return PTR_ERR(soc_dev); 100 101 ret = exynos_asv_init(&pdev->dev, regmap); 102 if (ret) 103 goto err; 104 105 platform_set_drvdata(pdev, soc_dev); 106 107 dev_info(soc_device_to_device(soc_dev), 108 "Exynos: CPU[%s] PRO_ID[0x%x] REV[0x%x] Detected\n", 109 soc_dev_attr->soc_id, product_id, revision); 110 111 return 0; 112 113 err: 114 soc_device_unregister(soc_dev); 115 116 return ret; 117 } 118 119 static int exynos_chipid_remove(struct platform_device *pdev) 120 { 121 struct soc_device *soc_dev = platform_get_drvdata(pdev); 122 123 soc_device_unregister(soc_dev); 124 125 return 0; 126 } 127 128 static const struct of_device_id exynos_chipid_of_device_ids[] = { 129 { .compatible = "samsung,exynos4210-chipid" }, 130 {} 131 }; 132 133 static struct platform_driver exynos_chipid_driver = { 134 .driver = { 135 .name = "exynos-chipid", 136 .of_match_table = exynos_chipid_of_device_ids, 137 }, 138 .probe = exynos_chipid_probe, 139 .remove = exynos_chipid_remove, 140 }; 141 builtin_platform_driver(exynos_chipid_driver); 142