diff options
Diffstat (limited to 'arch/arm/mach-mvebu/mvebu-soc-id.c')
-rw-r--r-- | arch/arm/mach-mvebu/mvebu-soc-id.c | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/arch/arm/mach-mvebu/mvebu-soc-id.c b/arch/arm/mach-mvebu/mvebu-soc-id.c new file mode 100644 index 000000000000..f3b325f6cbd4 --- /dev/null +++ b/arch/arm/mach-mvebu/mvebu-soc-id.c @@ -0,0 +1,119 @@ +/* + * ID and revision information for mvebu SoCs + * + * Copyright (C) 2014 Marvell + * + * Gregory CLEMENT <gregory.clement@free-electrons.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * All the mvebu SoCs have information related to their variant and + * revision that can be read from the PCI control register. This is + * done before the PCI initialization to avoid any conflict. Once the + * ID and revision are retrieved, the mapping is freed. + */ + +#define pr_fmt(fmt) "mvebu-soc-id: " fmt + +#include <linux/clk.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include "mvebu-soc-id.h" + +#define PCIE_DEV_ID_OFF 0x0 +#define PCIE_DEV_REV_OFF 0x8 + +#define SOC_ID_MASK 0xFFFF0000 +#define SOC_REV_MASK 0xFF + +static u32 soc_dev_id; +static u32 soc_rev; +static bool is_id_valid; + +static const struct of_device_id mvebu_pcie_of_match_table[] = { + { .compatible = "marvell,armada-xp-pcie", }, + { .compatible = "marvell,armada-370-pcie", }, + {}, +}; + +int mvebu_get_soc_id(u32 *dev, u32 *rev) +{ + if (is_id_valid) { + *dev = soc_dev_id; + *rev = soc_rev; + return 0; + } else + return -1; +} + +static int __init mvebu_soc_id_init(void) +{ + struct device_node *np; + int ret = 0; + void __iomem *pci_base; + struct clk *clk; + struct device_node *child; + + np = of_find_matching_node(NULL, mvebu_pcie_of_match_table); + if (!np) + return ret; + + /* + * ID and revision are available from any port, so we + * just pick the first one + */ + child = of_get_next_child(np, NULL); + if (child == NULL) { + pr_err("cannot get pci node\n"); + ret = -ENOMEM; + goto clk_err; + } + + clk = of_clk_get_by_name(child, NULL); + if (IS_ERR(clk)) { + pr_err("cannot get clock\n"); + ret = -ENOMEM; + goto clk_err; + } + + ret = clk_prepare_enable(clk); + if (ret) { + pr_err("cannot enable clock\n"); + goto clk_err; + } + + pci_base = of_iomap(child, 0); + if (pci_base == NULL) { + pr_err("cannot map registers\n"); + ret = -ENOMEM; + goto res_ioremap; + } + + /* SoC ID */ + soc_dev_id = readl(pci_base + PCIE_DEV_ID_OFF) >> 16; + + /* SoC revision */ + soc_rev = readl(pci_base + PCIE_DEV_REV_OFF) & SOC_REV_MASK; + + is_id_valid = true; + + pr_info("MVEBU SoC ID=0x%X, Rev=0x%X\n", soc_dev_id, soc_rev); + + iounmap(pci_base); + +res_ioremap: + clk_disable_unprepare(clk); + +clk_err: + of_node_put(child); + of_node_put(np); + + return ret; +} +core_initcall(mvebu_soc_id_init); + |