aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/soc
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2016-06-22 10:32:36 -0400
committerLinus Walleij <linus.walleij@linaro.org>2016-07-12 05:24:09 -0400
commit18a992787896fe822b2ea750e68d69cac0726739 (patch)
treeea1209023c76ca0c82606362afb99b337ba0e998 /drivers/soc
parentf15601d62bf1348c1dc9fe3455b277a652db1c6e (diff)
ARM: ux500: move soc_id driver to drivers/soc
As the ux500 id code is basically a standalone driver, we can move it out of the arch code into drivers/soc/ux500. This is a user-visible change, as it moves all the devices in sysfs from /sys/devices/soc0/ to /sys/devices/ and leaves the soc0 node as a separate device. Originally the idea was to put all on-chip devices under the soc node, and ux500 was the first platform to have this device, but later platforms almost all didn't follow that pattern, so this makes the platform do the same thing as everyone else. Since the platform is really obsolete now, I am optimistic that nothing will break after moving the devices around. As the SoC driver no longer has access to the private header files, I'm changing the code to instead look up the address of the backupram from devicetree, which is a good idea anyway. Finally, having a separate Kconfig symbol means the driver is now optional and could even be a loadable module rather than always being built-in if we allowed that for soc_device. Signed-off-by: Arnd Bergmann <arnd@arndb.de> [Fixup mising Makefile, fixup BB_UID_BASE to fc0] Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/soc')
-rw-r--r--drivers/soc/Kconfig1
-rw-r--r--drivers/soc/Makefile1
-rw-r--r--drivers/soc/ux500/Kconfig7
-rw-r--r--drivers/soc/ux500/Makefile1
-rw-r--r--drivers/soc/ux500/ux500-soc-id.c222
5 files changed, 232 insertions, 0 deletions
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index cb58ef0d9b2c..b9c1bf43ebec 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -10,6 +10,7 @@ source "drivers/soc/samsung/Kconfig"
10source "drivers/soc/sunxi/Kconfig" 10source "drivers/soc/sunxi/Kconfig"
11source "drivers/soc/tegra/Kconfig" 11source "drivers/soc/tegra/Kconfig"
12source "drivers/soc/ti/Kconfig" 12source "drivers/soc/ti/Kconfig"
13source "drivers/soc/ux500/Kconfig"
13source "drivers/soc/versatile/Kconfig" 14source "drivers/soc/versatile/Kconfig"
14 15
15endmenu 16endmenu
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 380230f03874..02359c95d7f3 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -15,4 +15,5 @@ obj-$(CONFIG_SOC_SAMSUNG) += samsung/
15obj-$(CONFIG_ARCH_SUNXI) += sunxi/ 15obj-$(CONFIG_ARCH_SUNXI) += sunxi/
16obj-$(CONFIG_ARCH_TEGRA) += tegra/ 16obj-$(CONFIG_ARCH_TEGRA) += tegra/
17obj-$(CONFIG_SOC_TI) += ti/ 17obj-$(CONFIG_SOC_TI) += ti/
18obj-$(CONFIG_ARCH_U8500) += ux500/
18obj-$(CONFIG_PLAT_VERSATILE) += versatile/ 19obj-$(CONFIG_PLAT_VERSATILE) += versatile/
diff --git a/drivers/soc/ux500/Kconfig b/drivers/soc/ux500/Kconfig
new file mode 100644
index 000000000000..025a44aef5db
--- /dev/null
+++ b/drivers/soc/ux500/Kconfig
@@ -0,0 +1,7 @@
1config UX500_SOC_ID
2 bool "SoC bus for ST-Ericsson ux500"
3 depends on ARCH_U8500 || COMPILE_TEST
4 default ARCH_U8500
5 help
6 Include support for the SoC bus on the ARM RealView platforms
7 providing some sysfs information about the ASIC variant.
diff --git a/drivers/soc/ux500/Makefile b/drivers/soc/ux500/Makefile
new file mode 100644
index 000000000000..0b87ad04b018
--- /dev/null
+++ b/drivers/soc/ux500/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_UX500_SOC_ID) += ux500-soc-id.o
diff --git a/drivers/soc/ux500/ux500-soc-id.c b/drivers/soc/ux500/ux500-soc-id.c
new file mode 100644
index 000000000000..6c1be74e5fcc
--- /dev/null
+++ b/drivers/soc/ux500/ux500-soc-id.c
@@ -0,0 +1,222 @@
1/*
2 * Copyright (C) ST-Ericsson SA 2010
3 *
4 * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson
5 * License terms: GNU General Public License (GPL) version 2
6 */
7
8#include <linux/kernel.h>
9#include <linux/init.h>
10#include <linux/io.h>
11#include <linux/module.h>
12#include <linux/random.h>
13#include <linux/slab.h>
14#include <linux/of.h>
15#include <linux/of_address.h>
16#include <linux/sys_soc.h>
17
18#include <asm/cputype.h>
19#include <asm/tlbflush.h>
20#include <asm/cacheflush.h>
21#include <asm/mach/map.h>
22
23/**
24 * struct dbx500_asic_id - fields of the ASIC ID
25 * @process: the manufacturing process, 0x40 is 40 nm 0x00 is "standard"
26 * @partnumber: hithereto 0x8500 for DB8500
27 * @revision: version code in the series
28 */
29struct dbx500_asic_id {
30 u16 partnumber;
31 u8 revision;
32 u8 process;
33};
34
35static struct dbx500_asic_id dbx500_id;
36
37static unsigned int __init ux500_read_asicid(phys_addr_t addr)
38{
39 void __iomem *virt = ioremap(addr, 4);
40 unsigned int asicid;
41
42 if (!virt)
43 return 0;
44
45 asicid = readl(virt);
46 iounmap(virt);
47
48 return asicid;
49}
50
51static void ux500_print_soc_info(unsigned int asicid)
52{
53 unsigned int rev = dbx500_id.revision;
54
55 pr_info("DB%4x ", dbx500_id.partnumber);
56
57 if (rev == 0x01)
58 pr_cont("Early Drop");
59 else if (rev >= 0xA0)
60 pr_cont("v%d.%d" , (rev >> 4) - 0xA + 1, rev & 0xf);
61 else
62 pr_cont("Unknown");
63
64 pr_cont(" [%#010x]\n", asicid);
65}
66
67static unsigned int partnumber(unsigned int asicid)
68{
69 return (asicid >> 8) & 0xffff;
70}
71
72/*
73 * SOC MIDR ASICID ADDRESS ASICID VALUE
74 * DB8500ed 0x410fc090 0x9001FFF4 0x00850001
75 * DB8500v1 0x411fc091 0x9001FFF4 0x008500A0
76 * DB8500v1.1 0x411fc091 0x9001FFF4 0x008500A1
77 * DB8500v2 0x412fc091 0x9001DBF4 0x008500B0
78 * DB8520v2.2 0x412fc091 0x9001DBF4 0x008500B2
79 * DB5500v1 0x412fc091 0x9001FFF4 0x005500A0
80 * DB9540 0x413fc090 0xFFFFDBF4 0x009540xx
81 */
82
83static void __init ux500_setup_id(void)
84{
85 unsigned int cpuid = read_cpuid_id();
86 unsigned int asicid = 0;
87 phys_addr_t addr = 0;
88
89 switch (cpuid) {
90 case 0x410fc090: /* DB8500ed */
91 case 0x411fc091: /* DB8500v1 */
92 addr = 0x9001FFF4;
93 break;
94
95 case 0x412fc091: /* DB8520 / DB8500v2 / DB5500v1 */
96 asicid = ux500_read_asicid(0x9001DBF4);
97 if (partnumber(asicid) == 0x8500 ||
98 partnumber(asicid) == 0x8520)
99 /* DB8500v2 */
100 break;
101
102 /* DB5500v1 */
103 addr = 0x9001FFF4;
104 break;
105
106 case 0x413fc090: /* DB9540 */
107 addr = 0xFFFFDBF4;
108 break;
109 }
110
111 if (addr)
112 asicid = ux500_read_asicid(addr);
113
114 if (!asicid) {
115 pr_err("Unable to identify SoC\n");
116 BUG();
117 }
118
119 dbx500_id.process = asicid >> 24;
120 dbx500_id.partnumber = partnumber(asicid);
121 dbx500_id.revision = asicid & 0xff;
122
123 ux500_print_soc_info(asicid);
124}
125
126static const char * __init ux500_get_machine(void)
127{
128 return kasprintf(GFP_KERNEL, "DB%4x", dbx500_id.partnumber);
129}
130
131static const char * __init ux500_get_family(void)
132{
133 return kasprintf(GFP_KERNEL, "ux500");
134}
135
136static const char * __init ux500_get_revision(void)
137{
138 unsigned int rev = dbx500_id.revision;
139
140 if (rev == 0x01)
141 return kasprintf(GFP_KERNEL, "%s", "ED");
142 else if (rev >= 0xA0)
143 return kasprintf(GFP_KERNEL, "%d.%d",
144 (rev >> 4) - 0xA + 1, rev & 0xf);
145
146 return kasprintf(GFP_KERNEL, "%s", "Unknown");
147}
148
149static ssize_t ux500_get_process(struct device *dev,
150 struct device_attribute *attr,
151 char *buf)
152{
153 if (dbx500_id.process == 0x00)
154 return sprintf(buf, "Standard\n");
155
156 return sprintf(buf, "%02xnm\n", dbx500_id.process);
157}
158
159static const char *db8500_read_soc_id(struct device_node *backupram)
160{
161 void __iomem *base;
162 void __iomem *uid;
163 const char *retstr;
164
165 base = of_iomap(backupram, 0);
166 if (!base)
167 return NULL;
168 uid = base + 0x1fc0;
169
170 /* Throw these device-specific numbers into the entropy pool */
171 add_device_randomness(uid, 0x14);
172 retstr = kasprintf(GFP_KERNEL, "%08x%08x%08x%08x%08x",
173 readl((u32 *)uid+0),
174 readl((u32 *)uid+1), readl((u32 *)uid+2),
175 readl((u32 *)uid+3), readl((u32 *)uid+4));
176 iounmap(base);
177 return retstr;
178}
179
180static void __init soc_info_populate(struct soc_device_attribute *soc_dev_attr,
181 struct device_node *backupram)
182{
183 soc_dev_attr->soc_id = db8500_read_soc_id(backupram);
184 soc_dev_attr->machine = ux500_get_machine();
185 soc_dev_attr->family = ux500_get_family();
186 soc_dev_attr->revision = ux500_get_revision();
187}
188
189static const struct device_attribute ux500_soc_attr =
190 __ATTR(process, S_IRUGO, ux500_get_process, NULL);
191
192static int __init ux500_soc_device_init(void)
193{
194 struct device *parent;
195 struct soc_device *soc_dev;
196 struct soc_device_attribute *soc_dev_attr;
197 struct device_node *backupram;
198
199 backupram = of_find_compatible_node(NULL, NULL, "ste,dbx500-backupram");
200 if (!backupram)
201 return 0;
202
203 ux500_setup_id();
204
205 soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
206 if (!soc_dev_attr)
207 return -ENOMEM;
208
209 soc_info_populate(soc_dev_attr, backupram);
210
211 soc_dev = soc_device_register(soc_dev_attr);
212 if (IS_ERR(soc_dev)) {
213 kfree(soc_dev_attr);
214 return PTR_ERR(soc_dev);
215 }
216
217 parent = soc_device_to_device(soc_dev);
218 device_create_file(parent, &ux500_soc_attr);
219
220 return 0;
221}
222subsys_initcall(ux500_soc_device_init);