|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Nuvoton WPCM450 SoC Identification | 
|  | * | 
|  | * Copyright (C) 2022 Jonathan Neuschäfer | 
|  | */ | 
|  |  | 
|  | #include <linux/mfd/syscon.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/regmap.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/sys_soc.h> | 
|  |  | 
|  | #define GCR_PDID	0 | 
|  | #define PDID_CHIP(x)	((x) & 0x00ffffff) | 
|  | #define CHIP_WPCM450	0x926450 | 
|  | #define PDID_REV(x)	((x) >> 24) | 
|  |  | 
|  | struct revision { | 
|  | u8 number; | 
|  | const char *name; | 
|  | }; | 
|  |  | 
|  | static const struct revision revisions[] __initconst = { | 
|  | { 0x00, "Z1" }, | 
|  | { 0x03, "Z2" }, | 
|  | { 0x04, "Z21" }, | 
|  | { 0x08, "A1" }, | 
|  | { 0x09, "A2" }, | 
|  | { 0x0a, "A3" }, | 
|  | {} | 
|  | }; | 
|  |  | 
|  | static const char * __init get_revision(unsigned int rev) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; revisions[i].name; i++) | 
|  | if (revisions[i].number == rev) | 
|  | return revisions[i].name; | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | static struct soc_device_attribute *wpcm450_attr; | 
|  | static struct soc_device *wpcm450_soc; | 
|  |  | 
|  | static int __init wpcm450_soc_init(void) | 
|  | { | 
|  | struct soc_device_attribute *attr; | 
|  | struct soc_device *soc; | 
|  | const char *revision; | 
|  | struct regmap *gcr; | 
|  | u32 pdid; | 
|  | int ret; | 
|  |  | 
|  | if (!of_machine_is_compatible("nuvoton,wpcm450")) | 
|  | return 0; | 
|  |  | 
|  | gcr = syscon_regmap_lookup_by_compatible("nuvoton,wpcm450-gcr"); | 
|  | if (IS_ERR(gcr)) | 
|  | return PTR_ERR(gcr); | 
|  | ret = regmap_read(gcr, GCR_PDID, &pdid); | 
|  | if (ret) | 
|  | return ret; | 
|  |  | 
|  | if (PDID_CHIP(pdid) != CHIP_WPCM450) { | 
|  | pr_warn("Unknown chip ID in GCR.PDID: 0x%06x\n", PDID_CHIP(pdid)); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | revision = get_revision(PDID_REV(pdid)); | 
|  | if (!revision) { | 
|  | pr_warn("Unknown chip revision in GCR.PDID: 0x%02x\n", PDID_REV(pdid)); | 
|  | return -ENODEV; | 
|  | } | 
|  |  | 
|  | attr = kzalloc(sizeof(*attr), GFP_KERNEL); | 
|  | if (!attr) | 
|  | return -ENOMEM; | 
|  |  | 
|  | attr->family = "Nuvoton NPCM"; | 
|  | attr->soc_id = "WPCM450"; | 
|  | attr->revision = revision; | 
|  | soc = soc_device_register(attr); | 
|  | if (IS_ERR(soc)) { | 
|  | kfree(attr); | 
|  | pr_warn("Could not register SoC device\n"); | 
|  | return PTR_ERR(soc); | 
|  | } | 
|  |  | 
|  | wpcm450_soc = soc; | 
|  | wpcm450_attr = attr; | 
|  | return 0; | 
|  | } | 
|  | module_init(wpcm450_soc_init); | 
|  |  | 
|  | static void __exit wpcm450_soc_exit(void) | 
|  | { | 
|  | if (wpcm450_soc) { | 
|  | soc_device_unregister(wpcm450_soc); | 
|  | wpcm450_soc = NULL; | 
|  | kfree(wpcm450_attr); | 
|  | } | 
|  | } | 
|  | module_exit(wpcm450_soc_exit); | 
|  |  | 
|  | MODULE_LICENSE("GPL"); | 
|  | MODULE_AUTHOR("Jonathan Neuschäfer"); | 
|  | MODULE_DESCRIPTION("Nuvoton WPCM450 SoC Identification driver"); |