1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * System controller support for Armada 370, 375 and XP platforms. |
4 | * |
5 | * Copyright (C) 2012 Marvell |
6 | * |
7 | * Lior Amsalem <alior@marvell.com> |
8 | * Gregory CLEMENT <gregory.clement@free-electrons.com> |
9 | * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> |
10 | * |
11 | * The Armada 370, 375 and Armada XP SoCs have a range of |
12 | * miscellaneous registers, that do not belong to a particular device, |
13 | * but rather provide system-level features. This basic |
14 | * system-controller driver provides a device tree binding for those |
15 | * registers, and implements utility functions offering various |
16 | * features related to those registers. |
17 | * |
18 | * For now, the feature set is limited to restarting the platform by a |
19 | * soft-reset, but it might be extended in the future. |
20 | */ |
21 | |
22 | #include <linux/kernel.h> |
23 | #include <linux/init.h> |
24 | #include <linux/of_address.h> |
25 | #include <linux/io.h> |
26 | #include <linux/reboot.h> |
27 | #include "common.h" |
28 | #include "mvebu-soc-id.h" |
29 | #include "pmsu.h" |
30 | |
31 | #define ARMADA_375_CRYPT0_ENG_TARGET 41 |
32 | #define ARMADA_375_CRYPT0_ENG_ATTR 1 |
33 | |
34 | static void __iomem *system_controller_base; |
35 | static phys_addr_t system_controller_phys_base; |
36 | |
37 | struct mvebu_system_controller { |
38 | u32 rstoutn_mask_offset; |
39 | u32 system_soft_reset_offset; |
40 | |
41 | u32 rstoutn_mask_reset_out_en; |
42 | u32 system_soft_reset; |
43 | |
44 | u32 resume_boot_addr; |
45 | |
46 | u32 dev_id; |
47 | u32 rev_id; |
48 | }; |
49 | static struct mvebu_system_controller *mvebu_sc; |
50 | |
51 | static const struct mvebu_system_controller armada_370_xp_system_controller = { |
52 | .rstoutn_mask_offset = 0x60, |
53 | .system_soft_reset_offset = 0x64, |
54 | .rstoutn_mask_reset_out_en = 0x1, |
55 | .system_soft_reset = 0x1, |
56 | .dev_id = 0x38, |
57 | .rev_id = 0x3c, |
58 | }; |
59 | |
60 | static const struct mvebu_system_controller armada_375_system_controller = { |
61 | .rstoutn_mask_offset = 0x54, |
62 | .system_soft_reset_offset = 0x58, |
63 | .rstoutn_mask_reset_out_en = 0x1, |
64 | .system_soft_reset = 0x1, |
65 | .resume_boot_addr = 0xd4, |
66 | .dev_id = 0x38, |
67 | .rev_id = 0x3c, |
68 | }; |
69 | |
70 | static const struct mvebu_system_controller orion_system_controller = { |
71 | .rstoutn_mask_offset = 0x108, |
72 | .system_soft_reset_offset = 0x10c, |
73 | .rstoutn_mask_reset_out_en = 0x4, |
74 | .system_soft_reset = 0x1, |
75 | }; |
76 | |
77 | static const struct of_device_id of_system_controller_table[] = { |
78 | { |
79 | .compatible = "marvell,orion-system-controller" , |
80 | .data = (void *) &orion_system_controller, |
81 | }, { |
82 | .compatible = "marvell,armada-370-xp-system-controller" , |
83 | .data = (void *) &armada_370_xp_system_controller, |
84 | }, { |
85 | .compatible = "marvell,armada-375-system-controller" , |
86 | .data = (void *) &armada_375_system_controller, |
87 | }, |
88 | { /* end of list */ }, |
89 | }; |
90 | |
91 | void mvebu_restart(enum reboot_mode mode, const char *cmd) |
92 | { |
93 | if (!system_controller_base) { |
94 | pr_err("Cannot restart, system-controller not available: check the device tree\n" ); |
95 | } else { |
96 | /* |
97 | * Enable soft reset to assert RSTOUTn. |
98 | */ |
99 | writel(val: mvebu_sc->rstoutn_mask_reset_out_en, |
100 | addr: system_controller_base + |
101 | mvebu_sc->rstoutn_mask_offset); |
102 | /* |
103 | * Assert soft reset. |
104 | */ |
105 | writel(val: mvebu_sc->system_soft_reset, |
106 | addr: system_controller_base + |
107 | mvebu_sc->system_soft_reset_offset); |
108 | } |
109 | |
110 | while (1) |
111 | ; |
112 | } |
113 | |
114 | int mvebu_system_controller_get_soc_id(u32 *dev, u32 *rev) |
115 | { |
116 | if (of_machine_is_compatible(compat: "marvell,armada380" ) && |
117 | system_controller_base) { |
118 | *dev = readl(addr: system_controller_base + mvebu_sc->dev_id) >> 16; |
119 | *rev = (readl(addr: system_controller_base + mvebu_sc->rev_id) >> 8) |
120 | & 0xF; |
121 | return 0; |
122 | } else |
123 | return -ENODEV; |
124 | } |
125 | |
126 | #if defined(CONFIG_SMP) && defined(CONFIG_MACH_MVEBU_V7) |
127 | static void mvebu_armada375_smp_wa_init(void) |
128 | { |
129 | u32 dev, rev; |
130 | phys_addr_t resume_addr_reg; |
131 | |
132 | if (mvebu_get_soc_id(&dev, &rev) != 0) |
133 | return; |
134 | |
135 | if (rev != ARMADA_375_Z1_REV) |
136 | return; |
137 | |
138 | resume_addr_reg = system_controller_phys_base + |
139 | mvebu_sc->resume_boot_addr; |
140 | mvebu_setup_boot_addr_wa(ARMADA_375_CRYPT0_ENG_TARGET, |
141 | ARMADA_375_CRYPT0_ENG_ATTR, |
142 | resume_addr_reg); |
143 | } |
144 | |
145 | void mvebu_system_controller_set_cpu_boot_addr(void *boot_addr) |
146 | { |
147 | BUG_ON(system_controller_base == NULL); |
148 | BUG_ON(mvebu_sc->resume_boot_addr == 0); |
149 | |
150 | if (of_machine_is_compatible("marvell,armada375" )) |
151 | mvebu_armada375_smp_wa_init(); |
152 | |
153 | writel(__pa_symbol(boot_addr), system_controller_base + |
154 | mvebu_sc->resume_boot_addr); |
155 | } |
156 | #endif |
157 | |
158 | static int __init mvebu_system_controller_init(void) |
159 | { |
160 | const struct of_device_id *match; |
161 | struct device_node *np; |
162 | |
163 | np = of_find_matching_node_and_match(NULL, matches: of_system_controller_table, |
164 | match: &match); |
165 | if (np) { |
166 | struct resource res; |
167 | system_controller_base = of_iomap(node: np, index: 0); |
168 | of_address_to_resource(dev: np, index: 0, r: &res); |
169 | system_controller_phys_base = res.start; |
170 | mvebu_sc = (struct mvebu_system_controller *)match->data; |
171 | of_node_put(node: np); |
172 | } |
173 | |
174 | return 0; |
175 | } |
176 | |
177 | early_initcall(mvebu_system_controller_init); |
178 | |