162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Nuvoton WPCM450 SoC Identification
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2022 Jonathan Neuschäfer
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
962306a36Sopenharmony_ci#include <linux/of.h>
1062306a36Sopenharmony_ci#include <linux/regmap.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/sys_soc.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#define GCR_PDID	0
1562306a36Sopenharmony_ci#define PDID_CHIP(x)	((x) & 0x00ffffff)
1662306a36Sopenharmony_ci#define CHIP_WPCM450	0x926450
1762306a36Sopenharmony_ci#define PDID_REV(x)	((x) >> 24)
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct revision {
2062306a36Sopenharmony_ci	u8 number;
2162306a36Sopenharmony_ci	const char *name;
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic const struct revision revisions[] __initconst = {
2562306a36Sopenharmony_ci	{ 0x00, "Z1" },
2662306a36Sopenharmony_ci	{ 0x03, "Z2" },
2762306a36Sopenharmony_ci	{ 0x04, "Z21" },
2862306a36Sopenharmony_ci	{ 0x08, "A1" },
2962306a36Sopenharmony_ci	{ 0x09, "A2" },
3062306a36Sopenharmony_ci	{ 0x0a, "A3" },
3162306a36Sopenharmony_ci	{}
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic const char * __init get_revision(unsigned int rev)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	int i;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	for (i = 0; revisions[i].name; i++)
3962306a36Sopenharmony_ci		if (revisions[i].number == rev)
4062306a36Sopenharmony_ci			return revisions[i].name;
4162306a36Sopenharmony_ci	return NULL;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic struct soc_device_attribute *wpcm450_attr;
4562306a36Sopenharmony_cistatic struct soc_device *wpcm450_soc;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int __init wpcm450_soc_init(void)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct soc_device_attribute *attr;
5062306a36Sopenharmony_ci	struct soc_device *soc;
5162306a36Sopenharmony_ci	const char *revision;
5262306a36Sopenharmony_ci	struct regmap *gcr;
5362306a36Sopenharmony_ci	u32 pdid;
5462306a36Sopenharmony_ci	int ret;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if (!of_machine_is_compatible("nuvoton,wpcm450"))
5762306a36Sopenharmony_ci		return 0;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	gcr = syscon_regmap_lookup_by_compatible("nuvoton,wpcm450-gcr");
6062306a36Sopenharmony_ci	if (IS_ERR(gcr))
6162306a36Sopenharmony_ci		return PTR_ERR(gcr);
6262306a36Sopenharmony_ci	ret = regmap_read(gcr, GCR_PDID, &pdid);
6362306a36Sopenharmony_ci	if (ret)
6462306a36Sopenharmony_ci		return ret;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (PDID_CHIP(pdid) != CHIP_WPCM450) {
6762306a36Sopenharmony_ci		pr_warn("Unknown chip ID in GCR.PDID: 0x%06x\n", PDID_CHIP(pdid));
6862306a36Sopenharmony_ci		return -ENODEV;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	revision = get_revision(PDID_REV(pdid));
7262306a36Sopenharmony_ci	if (!revision) {
7362306a36Sopenharmony_ci		pr_warn("Unknown chip revision in GCR.PDID: 0x%02x\n", PDID_REV(pdid));
7462306a36Sopenharmony_ci		return -ENODEV;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	attr = kzalloc(sizeof(*attr), GFP_KERNEL);
7862306a36Sopenharmony_ci	if (!attr)
7962306a36Sopenharmony_ci		return -ENOMEM;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	attr->family = "Nuvoton NPCM";
8262306a36Sopenharmony_ci	attr->soc_id = "WPCM450";
8362306a36Sopenharmony_ci	attr->revision = revision;
8462306a36Sopenharmony_ci	soc = soc_device_register(attr);
8562306a36Sopenharmony_ci	if (IS_ERR(soc)) {
8662306a36Sopenharmony_ci		kfree(attr);
8762306a36Sopenharmony_ci		pr_warn("Could not register SoC device\n");
8862306a36Sopenharmony_ci		return PTR_ERR(soc);
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	wpcm450_soc = soc;
9262306a36Sopenharmony_ci	wpcm450_attr = attr;
9362306a36Sopenharmony_ci	return 0;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_cimodule_init(wpcm450_soc_init);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void __exit wpcm450_soc_exit(void)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	if (wpcm450_soc) {
10062306a36Sopenharmony_ci		soc_device_unregister(wpcm450_soc);
10162306a36Sopenharmony_ci		wpcm450_soc = NULL;
10262306a36Sopenharmony_ci		kfree(wpcm450_attr);
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_cimodule_exit(wpcm450_soc_exit);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
10862306a36Sopenharmony_ciMODULE_AUTHOR("Jonathan Neuschäfer");
10962306a36Sopenharmony_ciMODULE_DESCRIPTION("Nuvoton WPCM450 SoC Identification driver");
110