162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright © 2014 NVIDIA Corporation
462306a36Sopenharmony_ci * Copyright © 2015 Broadcom Corporation
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/io.h>
862306a36Sopenharmony_ci#include <linux/of.h>
962306a36Sopenharmony_ci#include <linux/of_address.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <linux/soc/brcmstb/brcmstb.h>
1262306a36Sopenharmony_ci#include <linux/sys_soc.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic u32 family_id;
1562306a36Sopenharmony_cistatic u32 product_id;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ciu32 brcmstb_get_family_id(void)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	return family_id;
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ciEXPORT_SYMBOL(brcmstb_get_family_id);
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ciu32 brcmstb_get_product_id(void)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	return product_id;
2662306a36Sopenharmony_ci}
2762306a36Sopenharmony_ciEXPORT_SYMBOL(brcmstb_get_product_id);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic const struct of_device_id sun_top_ctrl_match[] = {
3062306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7125-sun-top-ctrl", },
3162306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7346-sun-top-ctrl", },
3262306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7358-sun-top-ctrl", },
3362306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7360-sun-top-ctrl", },
3462306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7362-sun-top-ctrl", },
3562306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7420-sun-top-ctrl", },
3662306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7425-sun-top-ctrl", },
3762306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7429-sun-top-ctrl", },
3862306a36Sopenharmony_ci	{ .compatible = "brcm,bcm7435-sun-top-ctrl", },
3962306a36Sopenharmony_ci	{ .compatible = "brcm,brcmstb-sun-top-ctrl", },
4062306a36Sopenharmony_ci	{ }
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic int __init brcmstb_soc_device_early_init(void)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct device_node *sun_top_ctrl;
4662306a36Sopenharmony_ci	void __iomem *sun_top_ctrl_base;
4762306a36Sopenharmony_ci	int ret = 0;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/* We could be on a multi-platform kernel, don't make this fatal but
5062306a36Sopenharmony_ci	 * bail out early
5162306a36Sopenharmony_ci	 */
5262306a36Sopenharmony_ci	sun_top_ctrl = of_find_matching_node(NULL, sun_top_ctrl_match);
5362306a36Sopenharmony_ci	if (!sun_top_ctrl)
5462306a36Sopenharmony_ci		return ret;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	sun_top_ctrl_base = of_iomap(sun_top_ctrl, 0);
5762306a36Sopenharmony_ci	if (!sun_top_ctrl_base) {
5862306a36Sopenharmony_ci		ret = -ENODEV;
5962306a36Sopenharmony_ci		goto out;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	family_id = readl(sun_top_ctrl_base);
6362306a36Sopenharmony_ci	product_id = readl(sun_top_ctrl_base + 0x4);
6462306a36Sopenharmony_ci	iounmap(sun_top_ctrl_base);
6562306a36Sopenharmony_ciout:
6662306a36Sopenharmony_ci	of_node_put(sun_top_ctrl);
6762306a36Sopenharmony_ci	return ret;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ciearly_initcall(brcmstb_soc_device_early_init);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic int __init brcmstb_soc_device_init(void)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct soc_device_attribute *soc_dev_attr;
7462306a36Sopenharmony_ci	struct device_node *sun_top_ctrl;
7562306a36Sopenharmony_ci	struct soc_device *soc_dev;
7662306a36Sopenharmony_ci	int ret = 0;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* We could be on a multi-platform kernel, don't make this fatal but
7962306a36Sopenharmony_ci	 * bail out early
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	sun_top_ctrl = of_find_matching_node(NULL, sun_top_ctrl_match);
8262306a36Sopenharmony_ci	if (!sun_top_ctrl)
8362306a36Sopenharmony_ci		return ret;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
8662306a36Sopenharmony_ci	if (!soc_dev_attr) {
8762306a36Sopenharmony_ci		ret = -ENOMEM;
8862306a36Sopenharmony_ci		goto out;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	soc_dev_attr->family = kasprintf(GFP_KERNEL, "%x",
9262306a36Sopenharmony_ci					 family_id >> 28 ?
9362306a36Sopenharmony_ci					 family_id >> 16 : family_id >> 8);
9462306a36Sopenharmony_ci	soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%x",
9562306a36Sopenharmony_ci					 product_id >> 28 ?
9662306a36Sopenharmony_ci					 product_id >> 16 : product_id >> 8);
9762306a36Sopenharmony_ci	soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%c%d",
9862306a36Sopenharmony_ci					 ((product_id & 0xf0) >> 4) + 'A',
9962306a36Sopenharmony_ci					   product_id & 0xf);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	soc_dev = soc_device_register(soc_dev_attr);
10262306a36Sopenharmony_ci	if (IS_ERR(soc_dev)) {
10362306a36Sopenharmony_ci		kfree(soc_dev_attr->family);
10462306a36Sopenharmony_ci		kfree(soc_dev_attr->soc_id);
10562306a36Sopenharmony_ci		kfree(soc_dev_attr->revision);
10662306a36Sopenharmony_ci		kfree(soc_dev_attr);
10762306a36Sopenharmony_ci		ret = -ENOMEM;
10862306a36Sopenharmony_ci	}
10962306a36Sopenharmony_ciout:
11062306a36Sopenharmony_ci	of_node_put(sun_top_ctrl);
11162306a36Sopenharmony_ci	return ret;
11262306a36Sopenharmony_ci}
11362306a36Sopenharmony_ciarch_initcall(brcmstb_soc_device_init);
114