1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Nuvoton WPCM450 SoC Identification |
4 | * |
5 | * Copyright (C) 2022 Jonathan Neuschäfer |
6 | */ |
7 | |
8 | #include <linux/mfd/syscon.h> |
9 | #include <linux/of.h> |
10 | #include <linux/regmap.h> |
11 | #include <linux/slab.h> |
12 | #include <linux/sys_soc.h> |
13 | |
14 | #define GCR_PDID 0 |
15 | #define PDID_CHIP(x) ((x) & 0x00ffffff) |
16 | #define CHIP_WPCM450 0x926450 |
17 | #define PDID_REV(x) ((x) >> 24) |
18 | |
19 | struct revision { |
20 | u8 number; |
21 | const char *name; |
22 | }; |
23 | |
24 | static const struct revision revisions[] __initconst = { |
25 | { 0x00, "Z1" }, |
26 | { 0x03, "Z2" }, |
27 | { 0x04, "Z21" }, |
28 | { 0x08, "A1" }, |
29 | { 0x09, "A2" }, |
30 | { 0x0a, "A3" }, |
31 | {} |
32 | }; |
33 | |
34 | static const char * __init get_revision(unsigned int rev) |
35 | { |
36 | int i; |
37 | |
38 | for (i = 0; revisions[i].name; i++) |
39 | if (revisions[i].number == rev) |
40 | return revisions[i].name; |
41 | return NULL; |
42 | } |
43 | |
44 | static struct soc_device_attribute *wpcm450_attr; |
45 | static struct soc_device *wpcm450_soc; |
46 | |
47 | static int __init wpcm450_soc_init(void) |
48 | { |
49 | struct soc_device_attribute *attr; |
50 | struct soc_device *soc; |
51 | const char *revision; |
52 | struct regmap *gcr; |
53 | u32 pdid; |
54 | int ret; |
55 | |
56 | if (!of_machine_is_compatible(compat: "nuvoton,wpcm450" )) |
57 | return 0; |
58 | |
59 | gcr = syscon_regmap_lookup_by_compatible(s: "nuvoton,wpcm450-gcr" ); |
60 | if (IS_ERR(ptr: gcr)) |
61 | return PTR_ERR(ptr: gcr); |
62 | ret = regmap_read(map: gcr, GCR_PDID, val: &pdid); |
63 | if (ret) |
64 | return ret; |
65 | |
66 | if (PDID_CHIP(pdid) != CHIP_WPCM450) { |
67 | pr_warn("Unknown chip ID in GCR.PDID: 0x%06x\n" , PDID_CHIP(pdid)); |
68 | return -ENODEV; |
69 | } |
70 | |
71 | revision = get_revision(PDID_REV(pdid)); |
72 | if (!revision) { |
73 | pr_warn("Unknown chip revision in GCR.PDID: 0x%02x\n" , PDID_REV(pdid)); |
74 | return -ENODEV; |
75 | } |
76 | |
77 | attr = kzalloc(size: sizeof(*attr), GFP_KERNEL); |
78 | if (!attr) |
79 | return -ENOMEM; |
80 | |
81 | attr->family = "Nuvoton NPCM" ; |
82 | attr->soc_id = "WPCM450" ; |
83 | attr->revision = revision; |
84 | soc = soc_device_register(soc_plat_dev_attr: attr); |
85 | if (IS_ERR(ptr: soc)) { |
86 | kfree(objp: attr); |
87 | pr_warn("Could not register SoC device\n" ); |
88 | return PTR_ERR(ptr: soc); |
89 | } |
90 | |
91 | wpcm450_soc = soc; |
92 | wpcm450_attr = attr; |
93 | return 0; |
94 | } |
95 | module_init(wpcm450_soc_init); |
96 | |
97 | static void __exit wpcm450_soc_exit(void) |
98 | { |
99 | if (wpcm450_soc) { |
100 | soc_device_unregister(soc_dev: wpcm450_soc); |
101 | wpcm450_soc = NULL; |
102 | kfree(objp: wpcm450_attr); |
103 | } |
104 | } |
105 | module_exit(wpcm450_soc_exit); |
106 | |
107 | MODULE_LICENSE("GPL" ); |
108 | MODULE_AUTHOR("Jonathan Neuschäfer" ); |
109 | MODULE_DESCRIPTION("Nuvoton WPCM450 SoC Identification driver" ); |
110 | |