mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 22:02:28 +00:00
af8d1c63af
All the mvebu SoCs have information related to their variant and
revision that can be read from the PCI control register.
This patch adds support for Armada XP and Armada 370. This reading of
the revision and the ID are done before the PCI initialization to
avoid any conflicts. Once these data are retrieved, the resources are
freed to let the PCI subsystem use it.
Cc: stable@vger.kernel.org # v3.12+
Fixes: 930ab3d403
(i2c: mv64xxx: Add I2C Transaction Generator support)
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Jason Cooper <jason@lakedaemon.net>
120 lines
2.5 KiB
C
120 lines
2.5 KiB
C
/*
|
|
* 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 (IS_ERR(pci_base)) {
|
|
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);
|
|
|