Commit | Line | Data |
---|---|---|
af8d1c63 GC |
1 | /* |
2 | * ID and revision information for mvebu SoCs | |
3 | * | |
4 | * Copyright (C) 2014 Marvell | |
5 | * | |
6 | * Gregory CLEMENT <gregory.clement@free-electrons.com> | |
7 | * | |
8 | * This file is licensed under the terms of the GNU General Public | |
9 | * License version 2. This program is licensed "as is" without any | |
10 | * warranty of any kind, whether express or implied. | |
11 | * | |
12 | * All the mvebu SoCs have information related to their variant and | |
13 | * revision that can be read from the PCI control register. This is | |
14 | * done before the PCI initialization to avoid any conflict. Once the | |
15 | * ID and revision are retrieved, the mapping is freed. | |
16 | */ | |
17 | ||
18 | #define pr_fmt(fmt) "mvebu-soc-id: " fmt | |
19 | ||
20 | #include <linux/clk.h> | |
21 | #include <linux/init.h> | |
22 | #include <linux/io.h> | |
23 | #include <linux/kernel.h> | |
24 | #include <linux/of.h> | |
25 | #include <linux/of_address.h> | |
26 | #include "mvebu-soc-id.h" | |
27 | ||
28 | #define PCIE_DEV_ID_OFF 0x0 | |
29 | #define PCIE_DEV_REV_OFF 0x8 | |
30 | ||
31 | #define SOC_ID_MASK 0xFFFF0000 | |
32 | #define SOC_REV_MASK 0xFF | |
33 | ||
34 | static u32 soc_dev_id; | |
35 | static u32 soc_rev; | |
36 | static bool is_id_valid; | |
37 | ||
38 | static const struct of_device_id mvebu_pcie_of_match_table[] = { | |
39 | { .compatible = "marvell,armada-xp-pcie", }, | |
40 | { .compatible = "marvell,armada-370-pcie", }, | |
97171ad7 | 41 | { .compatible = "marvell,kirkwood-pcie" }, |
af8d1c63 GC |
42 | {}, |
43 | }; | |
44 | ||
45 | int mvebu_get_soc_id(u32 *dev, u32 *rev) | |
46 | { | |
47 | if (is_id_valid) { | |
48 | *dev = soc_dev_id; | |
49 | *rev = soc_rev; | |
50 | return 0; | |
51 | } else | |
52 | return -1; | |
53 | } | |
54 | ||
55 | static int __init mvebu_soc_id_init(void) | |
56 | { | |
57 | struct device_node *np; | |
58 | int ret = 0; | |
59 | void __iomem *pci_base; | |
60 | struct clk *clk; | |
61 | struct device_node *child; | |
62 | ||
63 | np = of_find_matching_node(NULL, mvebu_pcie_of_match_table); | |
64 | if (!np) | |
65 | return ret; | |
66 | ||
67 | /* | |
68 | * ID and revision are available from any port, so we | |
69 | * just pick the first one | |
70 | */ | |
71 | child = of_get_next_child(np, NULL); | |
72 | if (child == NULL) { | |
73 | pr_err("cannot get pci node\n"); | |
74 | ret = -ENOMEM; | |
75 | goto clk_err; | |
76 | } | |
77 | ||
78 | clk = of_clk_get_by_name(child, NULL); | |
79 | if (IS_ERR(clk)) { | |
80 | pr_err("cannot get clock\n"); | |
81 | ret = -ENOMEM; | |
82 | goto clk_err; | |
83 | } | |
84 | ||
85 | ret = clk_prepare_enable(clk); | |
86 | if (ret) { | |
87 | pr_err("cannot enable clock\n"); | |
88 | goto clk_err; | |
89 | } | |
90 | ||
91 | pci_base = of_iomap(child, 0); | |
dc4910d9 | 92 | if (pci_base == NULL) { |
af8d1c63 GC |
93 | pr_err("cannot map registers\n"); |
94 | ret = -ENOMEM; | |
95 | goto res_ioremap; | |
96 | } | |
97 | ||
98 | /* SoC ID */ | |
99 | soc_dev_id = readl(pci_base + PCIE_DEV_ID_OFF) >> 16; | |
100 | ||
101 | /* SoC revision */ | |
102 | soc_rev = readl(pci_base + PCIE_DEV_REV_OFF) & SOC_REV_MASK; | |
103 | ||
104 | is_id_valid = true; | |
105 | ||
106 | pr_info("MVEBU SoC ID=0x%X, Rev=0x%X\n", soc_dev_id, soc_rev); | |
107 | ||
108 | iounmap(pci_base); | |
109 | ||
110 | res_ioremap: | |
111 | clk_disable_unprepare(clk); | |
112 | ||
113 | clk_err: | |
114 | of_node_put(child); | |
115 | of_node_put(np); | |
116 | ||
117 | return ret; | |
118 | } | |
119 | core_initcall(mvebu_soc_id_init); | |
120 |