1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ID and revision information for mvebu SoCs |
4 | * |
5 | * Copyright (C) 2014 Marvell |
6 | * |
7 | * Gregory CLEMENT <gregory.clement@free-electrons.com> |
8 | * |
9 | * All the mvebu SoCs have information related to their variant and |
10 | * revision that can be read from the PCI control register. This is |
11 | * done before the PCI initialization to avoid any conflict. Once the |
12 | * ID and revision are retrieved, the mapping is freed. |
13 | */ |
14 | |
15 | #define pr_fmt(fmt) "mvebu-soc-id: " fmt |
16 | |
17 | #include <linux/clk.h> |
18 | #include <linux/init.h> |
19 | #include <linux/io.h> |
20 | #include <linux/kernel.h> |
21 | #include <linux/of.h> |
22 | #include <linux/of_address.h> |
23 | #include <linux/slab.h> |
24 | #include <linux/sys_soc.h> |
25 | #include "common.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" , }, |
41 | { .compatible = "marvell,kirkwood-pcie" }, |
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 -ENODEV; |
53 | } |
54 | |
55 | static int __init get_soc_id_by_pci(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, matches: 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(node: 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(np: child, NULL); |
79 | if (IS_ERR(ptr: 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(node: child, index: 0); |
92 | if (pci_base == NULL) { |
93 | pr_err("cannot map registers\n" ); |
94 | ret = -ENOMEM; |
95 | goto res_ioremap; |
96 | } |
97 | |
98 | /* SoC ID */ |
99 | soc_dev_id = readl(addr: pci_base + PCIE_DEV_ID_OFF) >> 16; |
100 | |
101 | /* SoC revision */ |
102 | soc_rev = readl(addr: 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(addr: pci_base); |
109 | |
110 | res_ioremap: |
111 | /* |
112 | * If the PCIe unit is actually enabled and we have PCI |
113 | * support in the kernel, we intentionally do not release the |
114 | * reference to the clock. We want to keep it running since |
115 | * the bootloader does some PCIe link configuration that the |
116 | * kernel is for now unable to do, and gating the clock would |
117 | * make us loose this precious configuration. |
118 | */ |
119 | if (!of_device_is_available(device: child) || !IS_ENABLED(CONFIG_PCI_MVEBU)) { |
120 | clk_disable_unprepare(clk); |
121 | clk_put(clk); |
122 | } |
123 | |
124 | clk_err: |
125 | of_node_put(node: child); |
126 | of_node_put(node: np); |
127 | |
128 | return ret; |
129 | } |
130 | |
131 | static int __init mvebu_soc_id_init(void) |
132 | { |
133 | |
134 | /* |
135 | * First try to get the ID and the revision by the system |
136 | * register and use PCI registers only if it is not possible |
137 | */ |
138 | if (!mvebu_system_controller_get_soc_id(dev: &soc_dev_id, rev: &soc_rev)) { |
139 | is_id_valid = true; |
140 | pr_info("MVEBU SoC ID=0x%X, Rev=0x%X\n" , soc_dev_id, soc_rev); |
141 | return 0; |
142 | } |
143 | |
144 | return get_soc_id_by_pci(); |
145 | } |
146 | early_initcall(mvebu_soc_id_init); |
147 | |
148 | static int __init mvebu_soc_device(void) |
149 | { |
150 | struct soc_device_attribute *soc_dev_attr; |
151 | struct soc_device *soc_dev; |
152 | |
153 | /* Also protects against running on non-mvebu systems */ |
154 | if (!is_id_valid) |
155 | return 0; |
156 | |
157 | soc_dev_attr = kzalloc(size: sizeof(*soc_dev_attr), GFP_KERNEL); |
158 | if (!soc_dev_attr) |
159 | return -ENOMEM; |
160 | |
161 | soc_dev_attr->family = kasprintf(GFP_KERNEL, fmt: "Marvell" ); |
162 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, fmt: "%X" , soc_rev); |
163 | soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, fmt: "%X" , soc_dev_id); |
164 | |
165 | soc_dev = soc_device_register(soc_plat_dev_attr: soc_dev_attr); |
166 | if (IS_ERR(ptr: soc_dev)) { |
167 | kfree(objp: soc_dev_attr->family); |
168 | kfree(objp: soc_dev_attr->revision); |
169 | kfree(objp: soc_dev_attr->soc_id); |
170 | kfree(objp: soc_dev_attr); |
171 | } |
172 | |
173 | return 0; |
174 | } |
175 | postcore_initcall(mvebu_soc_device); |
176 | |