1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Freescale QorIQ Platforms GUTS Driver |
4 | * |
5 | * Copyright (C) 2016 Freescale Semiconductor, Inc. |
6 | */ |
7 | |
8 | #include <linux/io.h> |
9 | #include <linux/slab.h> |
10 | #include <linux/module.h> |
11 | #include <linux/of_fdt.h> |
12 | #include <linux/sys_soc.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/fsl/guts.h> |
16 | |
17 | struct fsl_soc_die_attr { |
18 | char *die; |
19 | u32 svr; |
20 | u32 mask; |
21 | }; |
22 | |
23 | struct fsl_soc_data { |
24 | const char *sfp_compat; |
25 | u32 uid_offset; |
26 | }; |
27 | |
28 | /* SoC die attribute definition for QorIQ platform */ |
29 | static const struct fsl_soc_die_attr fsl_soc_die[] = { |
30 | /* |
31 | * Power Architecture-based SoCs T Series |
32 | */ |
33 | |
34 | /* Die: T4240, SoC: T4240/T4160/T4080 */ |
35 | { .die = "T4240" , |
36 | .svr = 0x82400000, |
37 | .mask = 0xfff00000, |
38 | }, |
39 | /* Die: T1040, SoC: T1040/T1020/T1042/T1022 */ |
40 | { .die = "T1040" , |
41 | .svr = 0x85200000, |
42 | .mask = 0xfff00000, |
43 | }, |
44 | /* Die: T2080, SoC: T2080/T2081 */ |
45 | { .die = "T2080" , |
46 | .svr = 0x85300000, |
47 | .mask = 0xfff00000, |
48 | }, |
49 | /* Die: T1024, SoC: T1024/T1014/T1023/T1013 */ |
50 | { .die = "T1024" , |
51 | .svr = 0x85400000, |
52 | .mask = 0xfff00000, |
53 | }, |
54 | |
55 | /* |
56 | * ARM-based SoCs LS Series |
57 | */ |
58 | |
59 | /* Die: LS1043A, SoC: LS1043A/LS1023A */ |
60 | { .die = "LS1043A" , |
61 | .svr = 0x87920000, |
62 | .mask = 0xffff0000, |
63 | }, |
64 | /* Die: LS2080A, SoC: LS2080A/LS2040A/LS2085A */ |
65 | { .die = "LS2080A" , |
66 | .svr = 0x87010000, |
67 | .mask = 0xff3f0000, |
68 | }, |
69 | /* Die: LS1088A, SoC: LS1088A/LS1048A/LS1084A/LS1044A */ |
70 | { .die = "LS1088A" , |
71 | .svr = 0x87030000, |
72 | .mask = 0xff3f0000, |
73 | }, |
74 | /* Die: LS1012A, SoC: LS1012A */ |
75 | { .die = "LS1012A" , |
76 | .svr = 0x87040000, |
77 | .mask = 0xffff0000, |
78 | }, |
79 | /* Die: LS1046A, SoC: LS1046A/LS1026A */ |
80 | { .die = "LS1046A" , |
81 | .svr = 0x87070000, |
82 | .mask = 0xffff0000, |
83 | }, |
84 | /* Die: LS2088A, SoC: LS2088A/LS2048A/LS2084A/LS2044A */ |
85 | { .die = "LS2088A" , |
86 | .svr = 0x87090000, |
87 | .mask = 0xff3f0000, |
88 | }, |
89 | /* Die: LS1021A, SoC: LS1021A/LS1020A/LS1022A */ |
90 | { .die = "LS1021A" , |
91 | .svr = 0x87000000, |
92 | .mask = 0xfff70000, |
93 | }, |
94 | /* Die: LX2160A, SoC: LX2160A/LX2120A/LX2080A */ |
95 | { .die = "LX2160A" , |
96 | .svr = 0x87360000, |
97 | .mask = 0xff3f0000, |
98 | }, |
99 | /* Die: LS1028A, SoC: LS1028A */ |
100 | { .die = "LS1028A" , |
101 | .svr = 0x870b0000, |
102 | .mask = 0xff3f0000, |
103 | }, |
104 | { }, |
105 | }; |
106 | |
107 | static const struct fsl_soc_die_attr *fsl_soc_die_match( |
108 | u32 svr, const struct fsl_soc_die_attr *matches) |
109 | { |
110 | while (matches->svr) { |
111 | if (matches->svr == (svr & matches->mask)) |
112 | return matches; |
113 | matches++; |
114 | } |
115 | return NULL; |
116 | } |
117 | |
118 | static u64 fsl_guts_get_soc_uid(const char *compat, unsigned int offset) |
119 | { |
120 | struct device_node *np; |
121 | void __iomem *sfp_base; |
122 | u64 uid; |
123 | |
124 | np = of_find_compatible_node(NULL, NULL, compat); |
125 | if (!np) |
126 | return 0; |
127 | |
128 | sfp_base = of_iomap(node: np, index: 0); |
129 | if (!sfp_base) { |
130 | of_node_put(node: np); |
131 | return 0; |
132 | } |
133 | |
134 | uid = ioread32(sfp_base + offset); |
135 | uid <<= 32; |
136 | uid |= ioread32(sfp_base + offset + 4); |
137 | |
138 | iounmap(addr: sfp_base); |
139 | of_node_put(node: np); |
140 | |
141 | return uid; |
142 | } |
143 | |
144 | static const struct fsl_soc_data ls1028a_data = { |
145 | .sfp_compat = "fsl,ls1028a-sfp" , |
146 | .uid_offset = 0x21c, |
147 | }; |
148 | |
149 | /* |
150 | * Table for matching compatible strings, for device tree |
151 | * guts node, for Freescale QorIQ SOCs. |
152 | */ |
153 | static const struct of_device_id fsl_guts_of_match[] = { |
154 | { .compatible = "fsl,qoriq-device-config-1.0" , }, |
155 | { .compatible = "fsl,qoriq-device-config-2.0" , }, |
156 | { .compatible = "fsl,p1010-guts" , }, |
157 | { .compatible = "fsl,p1020-guts" , }, |
158 | { .compatible = "fsl,p1021-guts" , }, |
159 | { .compatible = "fsl,p1022-guts" , }, |
160 | { .compatible = "fsl,p1023-guts" , }, |
161 | { .compatible = "fsl,p2020-guts" , }, |
162 | { .compatible = "fsl,bsc9131-guts" , }, |
163 | { .compatible = "fsl,bsc9132-guts" , }, |
164 | { .compatible = "fsl,mpc8536-guts" , }, |
165 | { .compatible = "fsl,mpc8544-guts" , }, |
166 | { .compatible = "fsl,mpc8548-guts" , }, |
167 | { .compatible = "fsl,mpc8568-guts" , }, |
168 | { .compatible = "fsl,mpc8569-guts" , }, |
169 | { .compatible = "fsl,mpc8572-guts" , }, |
170 | { .compatible = "fsl,ls1021a-dcfg" , }, |
171 | { .compatible = "fsl,ls1043a-dcfg" , }, |
172 | { .compatible = "fsl,ls2080a-dcfg" , }, |
173 | { .compatible = "fsl,ls1088a-dcfg" , }, |
174 | { .compatible = "fsl,ls1012a-dcfg" , }, |
175 | { .compatible = "fsl,ls1046a-dcfg" , }, |
176 | { .compatible = "fsl,lx2160a-dcfg" , }, |
177 | { .compatible = "fsl,ls1028a-dcfg" , .data = &ls1028a_data}, |
178 | {} |
179 | }; |
180 | |
181 | static int __init fsl_guts_init(void) |
182 | { |
183 | struct soc_device_attribute *soc_dev_attr; |
184 | static struct soc_device *soc_dev; |
185 | const struct fsl_soc_die_attr *soc_die; |
186 | const struct fsl_soc_data *soc_data; |
187 | const struct of_device_id *match; |
188 | struct ccsr_guts __iomem *regs; |
189 | const char *machine = NULL; |
190 | struct device_node *np; |
191 | bool little_endian; |
192 | u64 soc_uid = 0; |
193 | u32 svr; |
194 | int ret; |
195 | |
196 | np = of_find_matching_node_and_match(NULL, matches: fsl_guts_of_match, match: &match); |
197 | if (!np) |
198 | return 0; |
199 | soc_data = match->data; |
200 | |
201 | regs = of_iomap(node: np, index: 0); |
202 | if (!regs) { |
203 | of_node_put(node: np); |
204 | return -ENOMEM; |
205 | } |
206 | |
207 | little_endian = of_property_read_bool(np, propname: "little-endian" ); |
208 | if (little_endian) |
209 | svr = ioread32(®s->svr); |
210 | else |
211 | svr = ioread32be(®s->svr); |
212 | iounmap(addr: regs); |
213 | of_node_put(node: np); |
214 | |
215 | /* Register soc device */ |
216 | soc_dev_attr = kzalloc(size: sizeof(*soc_dev_attr), GFP_KERNEL); |
217 | if (!soc_dev_attr) |
218 | return -ENOMEM; |
219 | |
220 | if (of_property_read_string(np: of_root, propname: "model" , out_string: &machine)) |
221 | of_property_read_string_index(np: of_root, propname: "compatible" , index: 0, output: &machine); |
222 | if (machine) { |
223 | soc_dev_attr->machine = kstrdup(s: machine, GFP_KERNEL); |
224 | if (!soc_dev_attr->machine) |
225 | goto err_nomem; |
226 | } |
227 | |
228 | soc_die = fsl_soc_die_match(svr, matches: fsl_soc_die); |
229 | if (soc_die) { |
230 | soc_dev_attr->family = kasprintf(GFP_KERNEL, fmt: "QorIQ %s" , |
231 | soc_die->die); |
232 | } else { |
233 | soc_dev_attr->family = kasprintf(GFP_KERNEL, fmt: "QorIQ" ); |
234 | } |
235 | if (!soc_dev_attr->family) |
236 | goto err_nomem; |
237 | |
238 | soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, fmt: "svr:0x%08x" , svr); |
239 | if (!soc_dev_attr->soc_id) |
240 | goto err_nomem; |
241 | |
242 | soc_dev_attr->revision = kasprintf(GFP_KERNEL, fmt: "%d.%d" , |
243 | (svr >> 4) & 0xf, svr & 0xf); |
244 | if (!soc_dev_attr->revision) |
245 | goto err_nomem; |
246 | |
247 | if (soc_data) |
248 | soc_uid = fsl_guts_get_soc_uid(compat: soc_data->sfp_compat, |
249 | offset: soc_data->uid_offset); |
250 | if (soc_uid) |
251 | soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, fmt: "%016llX" , |
252 | soc_uid); |
253 | |
254 | soc_dev = soc_device_register(soc_plat_dev_attr: soc_dev_attr); |
255 | if (IS_ERR(ptr: soc_dev)) { |
256 | ret = PTR_ERR(ptr: soc_dev); |
257 | goto err; |
258 | } |
259 | |
260 | pr_info("Machine: %s\n" , soc_dev_attr->machine); |
261 | pr_info("SoC family: %s\n" , soc_dev_attr->family); |
262 | pr_info("SoC ID: %s, Revision: %s\n" , |
263 | soc_dev_attr->soc_id, soc_dev_attr->revision); |
264 | |
265 | return 0; |
266 | |
267 | err_nomem: |
268 | ret = -ENOMEM; |
269 | err: |
270 | kfree(objp: soc_dev_attr->machine); |
271 | kfree(objp: soc_dev_attr->family); |
272 | kfree(objp: soc_dev_attr->soc_id); |
273 | kfree(objp: soc_dev_attr->revision); |
274 | kfree(objp: soc_dev_attr->serial_number); |
275 | kfree(objp: soc_dev_attr); |
276 | |
277 | return ret; |
278 | } |
279 | core_initcall(fsl_guts_init); |
280 | |