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> | |
56a705a4 AL |
26 | #include <linux/slab.h> |
27 | #include <linux/sys_soc.h> | |
9674d4a3 | 28 | #include "common.h" |
af8d1c63 GC |
29 | #include "mvebu-soc-id.h" |
30 | ||
31 | #define PCIE_DEV_ID_OFF 0x0 | |
32 | #define PCIE_DEV_REV_OFF 0x8 | |
33 | ||
34 | #define SOC_ID_MASK 0xFFFF0000 | |
35 | #define SOC_REV_MASK 0xFF | |
36 | ||
37 | static u32 soc_dev_id; | |
38 | static u32 soc_rev; | |
39 | static bool is_id_valid; | |
40 | ||
41 | static const struct of_device_id mvebu_pcie_of_match_table[] = { | |
42 | { .compatible = "marvell,armada-xp-pcie", }, | |
43 | { .compatible = "marvell,armada-370-pcie", }, | |
97171ad7 | 44 | { .compatible = "marvell,kirkwood-pcie" }, |
af8d1c63 GC |
45 | {}, |
46 | }; | |
47 | ||
48 | int mvebu_get_soc_id(u32 *dev, u32 *rev) | |
49 | { | |
50 | if (is_id_valid) { | |
51 | *dev = soc_dev_id; | |
52 | *rev = soc_rev; | |
53 | return 0; | |
54 | } else | |
5e80d81a | 55 | return -ENODEV; |
af8d1c63 GC |
56 | } |
57 | ||
9674d4a3 | 58 | static int __init get_soc_id_by_pci(void) |
af8d1c63 GC |
59 | { |
60 | struct device_node *np; | |
61 | int ret = 0; | |
62 | void __iomem *pci_base; | |
63 | struct clk *clk; | |
64 | struct device_node *child; | |
65 | ||
66 | np = of_find_matching_node(NULL, mvebu_pcie_of_match_table); | |
67 | if (!np) | |
68 | return ret; | |
69 | ||
70 | /* | |
71 | * ID and revision are available from any port, so we | |
72 | * just pick the first one | |
73 | */ | |
74 | child = of_get_next_child(np, NULL); | |
75 | if (child == NULL) { | |
76 | pr_err("cannot get pci node\n"); | |
77 | ret = -ENOMEM; | |
78 | goto clk_err; | |
79 | } | |
80 | ||
81 | clk = of_clk_get_by_name(child, NULL); | |
82 | if (IS_ERR(clk)) { | |
83 | pr_err("cannot get clock\n"); | |
84 | ret = -ENOMEM; | |
85 | goto clk_err; | |
86 | } | |
87 | ||
88 | ret = clk_prepare_enable(clk); | |
89 | if (ret) { | |
90 | pr_err("cannot enable clock\n"); | |
91 | goto clk_err; | |
92 | } | |
93 | ||
94 | pci_base = of_iomap(child, 0); | |
dc4910d9 | 95 | if (pci_base == NULL) { |
af8d1c63 GC |
96 | pr_err("cannot map registers\n"); |
97 | ret = -ENOMEM; | |
98 | goto res_ioremap; | |
99 | } | |
100 | ||
101 | /* SoC ID */ | |
102 | soc_dev_id = readl(pci_base + PCIE_DEV_ID_OFF) >> 16; | |
103 | ||
104 | /* SoC revision */ | |
105 | soc_rev = readl(pci_base + PCIE_DEV_REV_OFF) & SOC_REV_MASK; | |
106 | ||
107 | is_id_valid = true; | |
108 | ||
109 | pr_info("MVEBU SoC ID=0x%X, Rev=0x%X\n", soc_dev_id, soc_rev); | |
110 | ||
111 | iounmap(pci_base); | |
112 | ||
113 | res_ioremap: | |
b25bcf1b TP |
114 | /* |
115 | * If the PCIe unit is actually enabled and we have PCI | |
116 | * support in the kernel, we intentionally do not release the | |
117 | * reference to the clock. We want to keep it running since | |
118 | * the bootloader does some PCIe link configuration that the | |
119 | * kernel is for now unable to do, and gating the clock would | |
120 | * make us loose this precious configuration. | |
121 | */ | |
122 | if (!of_device_is_available(child) || !IS_ENABLED(CONFIG_PCI_MVEBU)) { | |
123 | clk_disable_unprepare(clk); | |
124 | clk_put(clk); | |
125 | } | |
af8d1c63 GC |
126 | |
127 | clk_err: | |
128 | of_node_put(child); | |
129 | of_node_put(np); | |
130 | ||
131 | return ret; | |
132 | } | |
9674d4a3 GC |
133 | |
134 | static int __init mvebu_soc_id_init(void) | |
135 | { | |
136 | ||
137 | /* | |
138 | * First try to get the ID and the revision by the system | |
139 | * register and use PCI registers only if it is not possible | |
140 | */ | |
141 | if (!mvebu_system_controller_get_soc_id(&soc_dev_id, &soc_rev)) { | |
142 | is_id_valid = true; | |
143 | pr_info("MVEBU SoC ID=0x%X, Rev=0x%X\n", soc_dev_id, soc_rev); | |
144 | return 0; | |
145 | } | |
146 | ||
147 | return get_soc_id_by_pci(); | |
148 | } | |
73c3c791 | 149 | early_initcall(mvebu_soc_id_init); |
af8d1c63 | 150 | |
56a705a4 AL |
151 | static int __init mvebu_soc_device(void) |
152 | { | |
153 | struct soc_device_attribute *soc_dev_attr; | |
154 | struct soc_device *soc_dev; | |
155 | ||
156 | /* Also protects against running on non-mvebu systems */ | |
157 | if (!is_id_valid) | |
158 | return 0; | |
159 | ||
160 | soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); | |
161 | if (!soc_dev_attr) | |
162 | return -ENOMEM; | |
163 | ||
164 | soc_dev_attr->family = kasprintf(GFP_KERNEL, "Marvell"); | |
165 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%X", soc_rev); | |
166 | soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%X", soc_dev_id); | |
167 | ||
168 | soc_dev = soc_device_register(soc_dev_attr); | |
169 | if (IS_ERR(soc_dev)) { | |
170 | kfree(soc_dev_attr->family); | |
171 | kfree(soc_dev_attr->revision); | |
172 | kfree(soc_dev_attr->soc_id); | |
173 | kfree(soc_dev_attr); | |
174 | } | |
175 | ||
176 | return 0; | |
177 | } | |
178 | postcore_initcall(mvebu_soc_device); |