162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ID and revision information for mvebu SoCs
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2014 Marvell
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * All the mvebu SoCs have information related to their variant and
1062306a36Sopenharmony_ci * revision that can be read from the PCI control register. This is
1162306a36Sopenharmony_ci * done before the PCI initialization to avoid any conflict. Once the
1262306a36Sopenharmony_ci * ID and revision are retrieved, the mapping is freed.
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define pr_fmt(fmt) "mvebu-soc-id: " fmt
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <linux/clk.h>
1862306a36Sopenharmony_ci#include <linux/init.h>
1962306a36Sopenharmony_ci#include <linux/io.h>
2062306a36Sopenharmony_ci#include <linux/kernel.h>
2162306a36Sopenharmony_ci#include <linux/of.h>
2262306a36Sopenharmony_ci#include <linux/of_address.h>
2362306a36Sopenharmony_ci#include <linux/slab.h>
2462306a36Sopenharmony_ci#include <linux/sys_soc.h>
2562306a36Sopenharmony_ci#include "common.h"
2662306a36Sopenharmony_ci#include "mvebu-soc-id.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define PCIE_DEV_ID_OFF		0x0
2962306a36Sopenharmony_ci#define PCIE_DEV_REV_OFF	0x8
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define SOC_ID_MASK	    0xFFFF0000
3262306a36Sopenharmony_ci#define SOC_REV_MASK	    0xFF
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic u32 soc_dev_id;
3562306a36Sopenharmony_cistatic u32 soc_rev;
3662306a36Sopenharmony_cistatic bool is_id_valid;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic const struct of_device_id mvebu_pcie_of_match_table[] = {
3962306a36Sopenharmony_ci	{ .compatible = "marvell,armada-xp-pcie", },
4062306a36Sopenharmony_ci	{ .compatible = "marvell,armada-370-pcie", },
4162306a36Sopenharmony_ci	{ .compatible = "marvell,kirkwood-pcie" },
4262306a36Sopenharmony_ci	{},
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciint mvebu_get_soc_id(u32 *dev, u32 *rev)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	if (is_id_valid) {
4862306a36Sopenharmony_ci		*dev = soc_dev_id;
4962306a36Sopenharmony_ci		*rev = soc_rev;
5062306a36Sopenharmony_ci		return 0;
5162306a36Sopenharmony_ci	} else
5262306a36Sopenharmony_ci		return -ENODEV;
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic int __init get_soc_id_by_pci(void)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct device_node *np;
5862306a36Sopenharmony_ci	int ret = 0;
5962306a36Sopenharmony_ci	void __iomem *pci_base;
6062306a36Sopenharmony_ci	struct clk *clk;
6162306a36Sopenharmony_ci	struct device_node *child;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	np = of_find_matching_node(NULL, mvebu_pcie_of_match_table);
6462306a36Sopenharmony_ci	if (!np)
6562306a36Sopenharmony_ci		return ret;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	/*
6862306a36Sopenharmony_ci	 * ID and revision are available from any port, so we
6962306a36Sopenharmony_ci	 * just pick the first one
7062306a36Sopenharmony_ci	 */
7162306a36Sopenharmony_ci	child = of_get_next_child(np, NULL);
7262306a36Sopenharmony_ci	if (child == NULL) {
7362306a36Sopenharmony_ci		pr_err("cannot get pci node\n");
7462306a36Sopenharmony_ci		ret = -ENOMEM;
7562306a36Sopenharmony_ci		goto clk_err;
7662306a36Sopenharmony_ci	}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	clk = of_clk_get_by_name(child, NULL);
7962306a36Sopenharmony_ci	if (IS_ERR(clk)) {
8062306a36Sopenharmony_ci		pr_err("cannot get clock\n");
8162306a36Sopenharmony_ci		ret = -ENOMEM;
8262306a36Sopenharmony_ci		goto clk_err;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	ret = clk_prepare_enable(clk);
8662306a36Sopenharmony_ci	if (ret) {
8762306a36Sopenharmony_ci		pr_err("cannot enable clock\n");
8862306a36Sopenharmony_ci		goto clk_err;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	pci_base = of_iomap(child, 0);
9262306a36Sopenharmony_ci	if (pci_base == NULL) {
9362306a36Sopenharmony_ci		pr_err("cannot map registers\n");
9462306a36Sopenharmony_ci		ret = -ENOMEM;
9562306a36Sopenharmony_ci		goto res_ioremap;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* SoC ID */
9962306a36Sopenharmony_ci	soc_dev_id = readl(pci_base + PCIE_DEV_ID_OFF) >> 16;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/* SoC revision */
10262306a36Sopenharmony_ci	soc_rev = readl(pci_base + PCIE_DEV_REV_OFF) & SOC_REV_MASK;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	is_id_valid = true;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	pr_info("MVEBU SoC ID=0x%X, Rev=0x%X\n", soc_dev_id, soc_rev);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	iounmap(pci_base);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cires_ioremap:
11162306a36Sopenharmony_ci	/*
11262306a36Sopenharmony_ci	 * If the PCIe unit is actually enabled and we have PCI
11362306a36Sopenharmony_ci	 * support in the kernel, we intentionally do not release the
11462306a36Sopenharmony_ci	 * reference to the clock. We want to keep it running since
11562306a36Sopenharmony_ci	 * the bootloader does some PCIe link configuration that the
11662306a36Sopenharmony_ci	 * kernel is for now unable to do, and gating the clock would
11762306a36Sopenharmony_ci	 * make us loose this precious configuration.
11862306a36Sopenharmony_ci	 */
11962306a36Sopenharmony_ci	if (!of_device_is_available(child) || !IS_ENABLED(CONFIG_PCI_MVEBU)) {
12062306a36Sopenharmony_ci		clk_disable_unprepare(clk);
12162306a36Sopenharmony_ci		clk_put(clk);
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ciclk_err:
12562306a36Sopenharmony_ci	of_node_put(child);
12662306a36Sopenharmony_ci	of_node_put(np);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	return ret;
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int __init mvebu_soc_id_init(void)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/*
13562306a36Sopenharmony_ci	 * First try to get the ID and the revision by the system
13662306a36Sopenharmony_ci	 * register and use PCI registers only if it is not possible
13762306a36Sopenharmony_ci	 */
13862306a36Sopenharmony_ci	if (!mvebu_system_controller_get_soc_id(&soc_dev_id, &soc_rev)) {
13962306a36Sopenharmony_ci		is_id_valid = true;
14062306a36Sopenharmony_ci		pr_info("MVEBU SoC ID=0x%X, Rev=0x%X\n", soc_dev_id, soc_rev);
14162306a36Sopenharmony_ci		return 0;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return get_soc_id_by_pci();
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ciearly_initcall(mvebu_soc_id_init);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic int __init mvebu_soc_device(void)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	struct soc_device_attribute *soc_dev_attr;
15162306a36Sopenharmony_ci	struct soc_device *soc_dev;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	/* Also protects against running on non-mvebu systems */
15462306a36Sopenharmony_ci	if (!is_id_valid)
15562306a36Sopenharmony_ci		return 0;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
15862306a36Sopenharmony_ci	if (!soc_dev_attr)
15962306a36Sopenharmony_ci		return -ENOMEM;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	soc_dev_attr->family = kasprintf(GFP_KERNEL, "Marvell");
16262306a36Sopenharmony_ci	soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%X", soc_rev);
16362306a36Sopenharmony_ci	soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%X", soc_dev_id);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	soc_dev = soc_device_register(soc_dev_attr);
16662306a36Sopenharmony_ci	if (IS_ERR(soc_dev)) {
16762306a36Sopenharmony_ci		kfree(soc_dev_attr->family);
16862306a36Sopenharmony_ci		kfree(soc_dev_attr->revision);
16962306a36Sopenharmony_ci		kfree(soc_dev_attr->soc_id);
17062306a36Sopenharmony_ci		kfree(soc_dev_attr);
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	return 0;
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_cipostcore_initcall(mvebu_soc_device);
176