1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2012 Freescale Semiconductor, Inc. |
4 | * Copyright 2012 Linaro Ltd. |
5 | */ |
6 | |
7 | #include <linux/clk.h> |
8 | #include <linux/clk/mxs.h> |
9 | #include <linux/clkdev.h> |
10 | #include <linux/delay.h> |
11 | #include <linux/err.h> |
12 | #include <linux/gpio.h> |
13 | #include <linux/init.h> |
14 | #include <linux/reboot.h> |
15 | #include <linux/micrel_phy.h> |
16 | #include <linux/of_address.h> |
17 | #include <linux/of_platform.h> |
18 | #include <linux/phy.h> |
19 | #include <linux/sys_soc.h> |
20 | #include <asm/mach/arch.h> |
21 | #include <asm/mach/map.h> |
22 | #include <asm/mach/time.h> |
23 | #include <asm/system_info.h> |
24 | #include <asm/system_misc.h> |
25 | |
26 | #include "pm.h" |
27 | |
28 | /* MXS DIGCTL SAIF CLKMUX */ |
29 | #define MXS_DIGCTL_SAIF_CLKMUX_DIRECT 0x0 |
30 | #define MXS_DIGCTL_SAIF_CLKMUX_CROSSINPUT 0x1 |
31 | #define MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR0 0x2 |
32 | #define MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR1 0x3 |
33 | |
34 | #define HW_DIGCTL_CHIPID 0x310 |
35 | #define HW_DIGCTL_CHIPID_MASK (0xffff << 16) |
36 | #define HW_DIGCTL_REV_MASK 0xff |
37 | #define HW_DIGCTL_CHIPID_MX23 (0x3780 << 16) |
38 | #define HW_DIGCTL_CHIPID_MX28 (0x2800 << 16) |
39 | |
40 | #define MXS_CHIP_REVISION_1_0 0x10 |
41 | #define MXS_CHIP_REVISION_1_1 0x11 |
42 | #define MXS_CHIP_REVISION_1_2 0x12 |
43 | #define MXS_CHIP_REVISION_1_3 0x13 |
44 | #define MXS_CHIP_REVISION_1_4 0x14 |
45 | #define MXS_CHIP_REV_UNKNOWN 0xff |
46 | |
47 | #define MXS_GPIO_NR(bank, nr) ((bank) * 32 + (nr)) |
48 | |
49 | #define MXS_SET_ADDR 0x4 |
50 | #define MXS_CLR_ADDR 0x8 |
51 | #define MXS_TOG_ADDR 0xc |
52 | |
53 | #define HW_OCOTP_OPS2 19 /* offset 0x150 */ |
54 | #define HW_OCOTP_OPS3 20 /* offset 0x160 */ |
55 | |
56 | static u32 chipid; |
57 | static u32 socid; |
58 | |
59 | static void __iomem *reset_addr; |
60 | |
61 | static inline void __mxs_setl(u32 mask, void __iomem *reg) |
62 | { |
63 | __raw_writel(val: mask, addr: reg + MXS_SET_ADDR); |
64 | } |
65 | |
66 | static inline void __mxs_clrl(u32 mask, void __iomem *reg) |
67 | { |
68 | __raw_writel(val: mask, addr: reg + MXS_CLR_ADDR); |
69 | } |
70 | |
71 | static inline void __mxs_togl(u32 mask, void __iomem *reg) |
72 | { |
73 | __raw_writel(val: mask, addr: reg + MXS_TOG_ADDR); |
74 | } |
75 | |
76 | #define OCOTP_WORD_OFFSET 0x20 |
77 | #define OCOTP_WORD_COUNT 0x20 |
78 | |
79 | #define BM_OCOTP_CTRL_BUSY (1 << 8) |
80 | #define BM_OCOTP_CTRL_ERROR (1 << 9) |
81 | #define BM_OCOTP_CTRL_RD_BANK_OPEN (1 << 12) |
82 | |
83 | static DEFINE_MUTEX(ocotp_mutex); |
84 | static u32 ocotp_words[OCOTP_WORD_COUNT]; |
85 | |
86 | static const u32 *mxs_get_ocotp(void) |
87 | { |
88 | struct device_node *np; |
89 | void __iomem *ocotp_base; |
90 | int timeout = 0x400; |
91 | size_t i; |
92 | static int once; |
93 | |
94 | if (once) |
95 | return ocotp_words; |
96 | |
97 | np = of_find_compatible_node(NULL, NULL, compat: "fsl,ocotp" ); |
98 | ocotp_base = of_iomap(node: np, index: 0); |
99 | WARN_ON(!ocotp_base); |
100 | |
101 | mutex_lock(&ocotp_mutex); |
102 | |
103 | /* |
104 | * clk_enable(hbus_clk) for ocotp can be skipped |
105 | * as it must be on when system is running. |
106 | */ |
107 | |
108 | /* try to clear ERROR bit */ |
109 | __mxs_clrl(BM_OCOTP_CTRL_ERROR, reg: ocotp_base); |
110 | |
111 | /* check both BUSY and ERROR cleared */ |
112 | while ((__raw_readl(addr: ocotp_base) & |
113 | (BM_OCOTP_CTRL_BUSY | BM_OCOTP_CTRL_ERROR)) && --timeout) |
114 | cpu_relax(); |
115 | |
116 | if (unlikely(!timeout)) |
117 | goto error_unlock; |
118 | |
119 | /* open OCOTP banks for read */ |
120 | __mxs_setl(BM_OCOTP_CTRL_RD_BANK_OPEN, reg: ocotp_base); |
121 | |
122 | /* approximately wait 32 hclk cycles */ |
123 | udelay(1); |
124 | |
125 | /* poll BUSY bit becoming cleared */ |
126 | timeout = 0x400; |
127 | while ((__raw_readl(addr: ocotp_base) & BM_OCOTP_CTRL_BUSY) && --timeout) |
128 | cpu_relax(); |
129 | |
130 | if (unlikely(!timeout)) |
131 | goto error_unlock; |
132 | |
133 | for (i = 0; i < OCOTP_WORD_COUNT; i++) |
134 | ocotp_words[i] = __raw_readl(addr: ocotp_base + OCOTP_WORD_OFFSET + |
135 | i * 0x10); |
136 | |
137 | /* close banks for power saving */ |
138 | __mxs_clrl(BM_OCOTP_CTRL_RD_BANK_OPEN, reg: ocotp_base); |
139 | |
140 | once = 1; |
141 | |
142 | mutex_unlock(lock: &ocotp_mutex); |
143 | |
144 | return ocotp_words; |
145 | |
146 | error_unlock: |
147 | mutex_unlock(lock: &ocotp_mutex); |
148 | pr_err("%s: timeout in reading OCOTP\n" , __func__); |
149 | return NULL; |
150 | } |
151 | |
152 | enum mac_oui { |
153 | OUI_FSL, |
154 | OUI_DENX, |
155 | OUI_CRYSTALFONTZ, |
156 | OUI_I2SE, |
157 | OUI_ARMADEUS, |
158 | }; |
159 | |
160 | static void __init update_fec_mac_prop(enum mac_oui oui) |
161 | { |
162 | struct device_node *np, *from = NULL; |
163 | struct property *newmac; |
164 | const u32 *ocotp = mxs_get_ocotp(); |
165 | u8 *macaddr; |
166 | u32 val; |
167 | int i; |
168 | |
169 | for (i = 0; i < 2; i++) { |
170 | np = of_find_compatible_node(from, NULL, compat: "fsl,imx28-fec" ); |
171 | if (!np) |
172 | return; |
173 | |
174 | from = np; |
175 | |
176 | if (of_property_present(np, propname: "local-mac-address" )) |
177 | continue; |
178 | |
179 | newmac = kzalloc(size: sizeof(*newmac) + 6, GFP_KERNEL); |
180 | if (!newmac) |
181 | return; |
182 | newmac->value = newmac + 1; |
183 | newmac->length = 6; |
184 | |
185 | newmac->name = kstrdup(s: "local-mac-address" , GFP_KERNEL); |
186 | if (!newmac->name) { |
187 | kfree(objp: newmac); |
188 | return; |
189 | } |
190 | |
191 | /* |
192 | * OCOTP only stores the last 4 octets for each mac address, |
193 | * so hard-code OUI here. |
194 | */ |
195 | macaddr = newmac->value; |
196 | switch (oui) { |
197 | case OUI_FSL: |
198 | macaddr[0] = 0x00; |
199 | macaddr[1] = 0x04; |
200 | macaddr[2] = 0x9f; |
201 | break; |
202 | case OUI_DENX: |
203 | macaddr[0] = 0xc0; |
204 | macaddr[1] = 0xe5; |
205 | macaddr[2] = 0x4e; |
206 | break; |
207 | case OUI_CRYSTALFONTZ: |
208 | macaddr[0] = 0x58; |
209 | macaddr[1] = 0xb9; |
210 | macaddr[2] = 0xe1; |
211 | break; |
212 | case OUI_I2SE: |
213 | macaddr[0] = 0x00; |
214 | macaddr[1] = 0x01; |
215 | macaddr[2] = 0x87; |
216 | break; |
217 | case OUI_ARMADEUS: |
218 | macaddr[0] = 0x00; |
219 | macaddr[1] = 0x1e; |
220 | macaddr[2] = 0xac; |
221 | break; |
222 | } |
223 | val = ocotp[i]; |
224 | macaddr[3] = (val >> 16) & 0xff; |
225 | macaddr[4] = (val >> 8) & 0xff; |
226 | macaddr[5] = (val >> 0) & 0xff; |
227 | |
228 | of_update_property(np, newprop: newmac); |
229 | } |
230 | } |
231 | |
232 | static inline void enable_clk_enet_out(void) |
233 | { |
234 | struct clk *clk = clk_get_sys(dev_id: "enet_out" , NULL); |
235 | |
236 | if (!IS_ERR(ptr: clk)) |
237 | clk_prepare_enable(clk); |
238 | } |
239 | |
240 | static void __init imx28_evk_init(void) |
241 | { |
242 | update_fec_mac_prop(oui: OUI_FSL); |
243 | |
244 | mxs_saif_clkmux_select(MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR0); |
245 | } |
246 | |
247 | static void __init imx28_apf28_init(void) |
248 | { |
249 | update_fec_mac_prop(oui: OUI_ARMADEUS); |
250 | } |
251 | |
252 | static int apx4devkit_phy_fixup(struct phy_device *phy) |
253 | { |
254 | phy->dev_flags |= MICREL_PHY_50MHZ_CLK; |
255 | return 0; |
256 | } |
257 | |
258 | static void __init apx4devkit_init(void) |
259 | { |
260 | enable_clk_enet_out(); |
261 | |
262 | if (IS_BUILTIN(CONFIG_PHYLIB)) |
263 | phy_register_fixup_for_uid(PHY_ID_KSZ8051, MICREL_PHY_ID_MASK, |
264 | run: apx4devkit_phy_fixup); |
265 | } |
266 | |
267 | static void __init crystalfontz_init(void) |
268 | { |
269 | update_fec_mac_prop(oui: OUI_CRYSTALFONTZ); |
270 | } |
271 | |
272 | static void __init duckbill_init(void) |
273 | { |
274 | update_fec_mac_prop(oui: OUI_I2SE); |
275 | } |
276 | |
277 | static void __init m28cu3_init(void) |
278 | { |
279 | update_fec_mac_prop(oui: OUI_DENX); |
280 | } |
281 | |
282 | static const char __init *mxs_get_soc_id(void) |
283 | { |
284 | struct device_node *np; |
285 | void __iomem *digctl_base; |
286 | |
287 | np = of_find_compatible_node(NULL, NULL, compat: "fsl,imx23-digctl" ); |
288 | digctl_base = of_iomap(node: np, index: 0); |
289 | WARN_ON(!digctl_base); |
290 | |
291 | chipid = readl(addr: digctl_base + HW_DIGCTL_CHIPID); |
292 | socid = chipid & HW_DIGCTL_CHIPID_MASK; |
293 | |
294 | iounmap(addr: digctl_base); |
295 | of_node_put(node: np); |
296 | |
297 | switch (socid) { |
298 | case HW_DIGCTL_CHIPID_MX23: |
299 | return "i.MX23" ; |
300 | case HW_DIGCTL_CHIPID_MX28: |
301 | return "i.MX28" ; |
302 | default: |
303 | return "Unknown" ; |
304 | } |
305 | } |
306 | |
307 | static u32 __init mxs_get_cpu_rev(void) |
308 | { |
309 | u32 rev = chipid & HW_DIGCTL_REV_MASK; |
310 | |
311 | switch (socid) { |
312 | case HW_DIGCTL_CHIPID_MX23: |
313 | switch (rev) { |
314 | case 0x0: |
315 | return MXS_CHIP_REVISION_1_0; |
316 | case 0x1: |
317 | return MXS_CHIP_REVISION_1_1; |
318 | case 0x2: |
319 | return MXS_CHIP_REVISION_1_2; |
320 | case 0x3: |
321 | return MXS_CHIP_REVISION_1_3; |
322 | case 0x4: |
323 | return MXS_CHIP_REVISION_1_4; |
324 | default: |
325 | return MXS_CHIP_REV_UNKNOWN; |
326 | } |
327 | case HW_DIGCTL_CHIPID_MX28: |
328 | switch (rev) { |
329 | case 0x0: |
330 | return MXS_CHIP_REVISION_1_1; |
331 | case 0x1: |
332 | return MXS_CHIP_REVISION_1_2; |
333 | default: |
334 | return MXS_CHIP_REV_UNKNOWN; |
335 | } |
336 | default: |
337 | return MXS_CHIP_REV_UNKNOWN; |
338 | } |
339 | } |
340 | |
341 | static const char __init *mxs_get_revision(void) |
342 | { |
343 | u32 rev = mxs_get_cpu_rev(); |
344 | |
345 | if (rev != MXS_CHIP_REV_UNKNOWN) |
346 | return kasprintf(GFP_KERNEL, fmt: "%d.%d" , (rev >> 4) & 0xf, |
347 | rev & 0xf); |
348 | else |
349 | return kasprintf(GFP_KERNEL, fmt: "%s" , "Unknown" ); |
350 | } |
351 | |
352 | #define MX23_CLKCTRL_RESET_OFFSET 0x120 |
353 | #define MX28_CLKCTRL_RESET_OFFSET 0x1e0 |
354 | |
355 | static int __init mxs_restart_init(void) |
356 | { |
357 | struct device_node *np; |
358 | |
359 | np = of_find_compatible_node(NULL, NULL, compat: "fsl,imx23-clkctrl" ); |
360 | if (!np) |
361 | np = of_find_compatible_node(NULL, NULL, compat: "fsl,imx28-clkctrl" ); |
362 | reset_addr = of_iomap(node: np, index: 0); |
363 | if (!reset_addr) |
364 | return -ENODEV; |
365 | |
366 | if (of_device_is_compatible(device: np, "fsl,imx23-clkctrl" )) |
367 | reset_addr += MX23_CLKCTRL_RESET_OFFSET; |
368 | else |
369 | reset_addr += MX28_CLKCTRL_RESET_OFFSET; |
370 | of_node_put(node: np); |
371 | |
372 | return 0; |
373 | } |
374 | |
375 | static void __init eukrea_mbmx283lc_init(void) |
376 | { |
377 | mxs_saif_clkmux_select(MXS_DIGCTL_SAIF_CLKMUX_EXTMSTR0); |
378 | } |
379 | |
380 | static void __init mxs_machine_init(void) |
381 | { |
382 | struct device_node *root; |
383 | struct device *parent; |
384 | struct soc_device *soc_dev; |
385 | struct soc_device_attribute *soc_dev_attr; |
386 | u64 soc_uid = 0; |
387 | const u32 *ocotp = mxs_get_ocotp(); |
388 | int ret; |
389 | |
390 | soc_dev_attr = kzalloc(size: sizeof(*soc_dev_attr), GFP_KERNEL); |
391 | if (!soc_dev_attr) |
392 | return; |
393 | |
394 | root = of_find_node_by_path(path: "/" ); |
395 | ret = of_property_read_string(np: root, propname: "model" , out_string: &soc_dev_attr->machine); |
396 | if (ret) { |
397 | kfree(objp: soc_dev_attr); |
398 | return; |
399 | } |
400 | |
401 | soc_dev_attr->family = "Freescale MXS Family" ; |
402 | soc_dev_attr->soc_id = mxs_get_soc_id(); |
403 | soc_dev_attr->revision = mxs_get_revision(); |
404 | |
405 | if (socid == HW_DIGCTL_CHIPID_MX23) { |
406 | soc_uid = system_serial_low = ocotp[HW_OCOTP_OPS3]; |
407 | } else if (socid == HW_DIGCTL_CHIPID_MX28) { |
408 | soc_uid = system_serial_high = ocotp[HW_OCOTP_OPS2]; |
409 | soc_uid <<= 32; |
410 | system_serial_low = ocotp[HW_OCOTP_OPS3]; |
411 | soc_uid |= system_serial_low; |
412 | } |
413 | |
414 | if (soc_uid) |
415 | soc_dev_attr->serial_number = kasprintf(GFP_KERNEL, fmt: "%016llX" , soc_uid); |
416 | |
417 | soc_dev = soc_device_register(soc_plat_dev_attr: soc_dev_attr); |
418 | if (IS_ERR(ptr: soc_dev)) { |
419 | kfree(objp: soc_dev_attr->serial_number); |
420 | kfree(objp: soc_dev_attr->revision); |
421 | kfree(objp: soc_dev_attr); |
422 | return; |
423 | } |
424 | |
425 | parent = soc_device_to_device(soc: soc_dev); |
426 | |
427 | if (of_machine_is_compatible(compat: "fsl,imx28-evk" )) |
428 | imx28_evk_init(); |
429 | if (of_machine_is_compatible(compat: "armadeus,imx28-apf28" )) |
430 | imx28_apf28_init(); |
431 | else if (of_machine_is_compatible(compat: "bluegiga,apx4devkit" )) |
432 | apx4devkit_init(); |
433 | else if (of_machine_is_compatible(compat: "crystalfontz,cfa10036" )) |
434 | crystalfontz_init(); |
435 | else if (of_machine_is_compatible(compat: "eukrea,mbmx283lc" )) |
436 | eukrea_mbmx283lc_init(); |
437 | else if (of_machine_is_compatible(compat: "i2se,duckbill" ) || |
438 | of_machine_is_compatible(compat: "i2se,duckbill-2" )) |
439 | duckbill_init(); |
440 | else if (of_machine_is_compatible(compat: "msr,m28cu3" )) |
441 | m28cu3_init(); |
442 | |
443 | of_platform_default_populate(NULL, NULL, parent); |
444 | |
445 | mxs_restart_init(); |
446 | } |
447 | |
448 | #define MXS_CLKCTRL_RESET_CHIP (1 << 1) |
449 | |
450 | /* |
451 | * Reset the system. It is called by machine_restart(). |
452 | */ |
453 | static void mxs_restart(enum reboot_mode mode, const char *cmd) |
454 | { |
455 | if (reset_addr) { |
456 | /* reset the chip */ |
457 | __mxs_setl(MXS_CLKCTRL_RESET_CHIP, reg: reset_addr); |
458 | |
459 | pr_err("Failed to assert the chip reset\n" ); |
460 | |
461 | /* Delay to allow the serial port to show the message */ |
462 | mdelay(50); |
463 | } |
464 | |
465 | /* We'll take a jump through zero as a poor second */ |
466 | soft_restart(0); |
467 | } |
468 | |
469 | static const char *const mxs_dt_compat[] __initconst = { |
470 | "fsl,imx28" , |
471 | "fsl,imx23" , |
472 | NULL, |
473 | }; |
474 | |
475 | DT_MACHINE_START(MXS, "Freescale MXS (Device Tree)" ) |
476 | .init_machine = mxs_machine_init, |
477 | .init_late = mxs_pm_init, |
478 | .dt_compat = mxs_dt_compat, |
479 | .restart = mxs_restart, |
480 | MACHINE_END |
481 | |