Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/arm/mach-mvebu/mvebu-soc-id.c
26292 views
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 <[email protected]>
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, 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);
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(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
/*
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(child) || !IS_ENABLED(CONFIG_PCI_MVEBU)) {
120
clk_disable_unprepare(clk);
121
clk_put(clk);
122
}
123
124
clk_err:
125
of_node_put(child);
126
of_node_put(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(&soc_dev_id, &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(sizeof(*soc_dev_attr), GFP_KERNEL);
158
if (!soc_dev_attr)
159
return -ENOMEM;
160
161
soc_dev_attr->family = kasprintf(GFP_KERNEL, "Marvell");
162
soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%X", soc_rev);
163
soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%X", soc_dev_id);
164
165
soc_dev = soc_device_register(soc_dev_attr);
166
if (IS_ERR(soc_dev)) {
167
kfree(soc_dev_attr->family);
168
kfree(soc_dev_attr->revision);
169
kfree(soc_dev_attr->soc_id);
170
kfree(soc_dev_attr);
171
}
172
173
return 0;
174
}
175
postcore_initcall(mvebu_soc_device);
176
177