1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2011 Freescale Semiconductor, Inc. |
4 | * Copyright 2011 Linaro Ltd. |
5 | */ |
6 | |
7 | #include <linux/init.h> |
8 | #include <linux/io.h> |
9 | #include <linux/iopoll.h> |
10 | #include <linux/of.h> |
11 | #include <linux/of_address.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/reset-controller.h> |
14 | #include <linux/smp.h> |
15 | #include <asm/smp_plat.h> |
16 | #include "common.h" |
17 | #include "hardware.h" |
18 | |
19 | #define SRC_SCR 0x000 |
20 | #define SRC_GPR1_V1 0x020 |
21 | #define SRC_GPR1_V2 0x074 |
22 | #define SRC_GPR1(gpr_v2) ((gpr_v2) ? SRC_GPR1_V2 : SRC_GPR1_V1) |
23 | #define BP_SRC_SCR_WARM_RESET_ENABLE 0 |
24 | #define BP_SRC_SCR_SW_GPU_RST 1 |
25 | #define BP_SRC_SCR_SW_VPU_RST 2 |
26 | #define BP_SRC_SCR_SW_IPU1_RST 3 |
27 | #define BP_SRC_SCR_SW_OPEN_VG_RST 4 |
28 | #define BP_SRC_SCR_SW_IPU2_RST 12 |
29 | #define BP_SRC_SCR_CORE1_RST 14 |
30 | #define BP_SRC_SCR_CORE1_ENABLE 22 |
31 | /* below is for i.MX7D */ |
32 | #define SRC_A7RCR1 0x008 |
33 | #define BP_SRC_A7RCR1_A7_CORE1_ENABLE 1 |
34 | #define GPC_CPU_PGC_SW_PUP_REQ 0xf0 |
35 | #define GPC_CPU_PGC_SW_PDN_REQ 0xfc |
36 | #define GPC_PGC_C1 0x840 |
37 | #define BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7 0x2 |
38 | |
39 | static void __iomem *src_base; |
40 | static DEFINE_SPINLOCK(scr_lock); |
41 | static bool gpr_v2; |
42 | static void __iomem *gpc_base; |
43 | |
44 | static const int sw_reset_bits[5] = { |
45 | BP_SRC_SCR_SW_GPU_RST, |
46 | BP_SRC_SCR_SW_VPU_RST, |
47 | BP_SRC_SCR_SW_IPU1_RST, |
48 | BP_SRC_SCR_SW_OPEN_VG_RST, |
49 | BP_SRC_SCR_SW_IPU2_RST |
50 | }; |
51 | |
52 | static int imx_src_reset_module(struct reset_controller_dev *rcdev, |
53 | unsigned long sw_reset_idx) |
54 | { |
55 | unsigned long timeout; |
56 | unsigned long flags; |
57 | int bit; |
58 | u32 val; |
59 | |
60 | if (sw_reset_idx >= ARRAY_SIZE(sw_reset_bits)) |
61 | return -EINVAL; |
62 | |
63 | bit = 1 << sw_reset_bits[sw_reset_idx]; |
64 | |
65 | spin_lock_irqsave(&scr_lock, flags); |
66 | val = readl_relaxed(src_base + SRC_SCR); |
67 | val |= bit; |
68 | writel_relaxed(val, src_base + SRC_SCR); |
69 | spin_unlock_irqrestore(lock: &scr_lock, flags); |
70 | |
71 | timeout = jiffies + msecs_to_jiffies(m: 1000); |
72 | while (readl(addr: src_base + SRC_SCR) & bit) { |
73 | if (time_after(jiffies, timeout)) |
74 | return -ETIME; |
75 | cpu_relax(); |
76 | } |
77 | |
78 | return 0; |
79 | } |
80 | |
81 | static const struct reset_control_ops imx_src_ops = { |
82 | .reset = imx_src_reset_module, |
83 | }; |
84 | |
85 | static void imx_gpcv2_set_m_core_pgc(bool enable, u32 offset) |
86 | { |
87 | writel_relaxed(enable, gpc_base + offset); |
88 | } |
89 | |
90 | /* |
91 | * The motivation for bringing up the second i.MX7D core inside the kernel |
92 | * is that legacy vendor bootloaders usually do not implement PSCI support. |
93 | * This is a significant blocker for systems in the field that are running old |
94 | * bootloader versions to upgrade to a modern mainline kernel version, as only |
95 | * one CPU of the i.MX7D would be brought up. |
96 | * Bring up the second i.MX7D core inside the kernel to make the migration |
97 | * path to mainline kernel easier for the existing iMX7D users. |
98 | */ |
99 | void imx_gpcv2_set_core1_pdn_pup_by_software(bool pdn) |
100 | { |
101 | u32 reg = pdn ? GPC_CPU_PGC_SW_PDN_REQ : GPC_CPU_PGC_SW_PUP_REQ; |
102 | u32 val, pup; |
103 | int ret; |
104 | |
105 | imx_gpcv2_set_m_core_pgc(enable: true, GPC_PGC_C1); |
106 | val = readl_relaxed(gpc_base + reg); |
107 | val |= BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7; |
108 | writel_relaxed(val, gpc_base + reg); |
109 | |
110 | ret = readl_relaxed_poll_timeout_atomic(gpc_base + reg, pup, |
111 | !(pup & BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7), |
112 | 5, 1000000); |
113 | if (ret < 0) { |
114 | pr_err("i.MX7D: CORE1_A7 power up timeout\n" ); |
115 | val &= ~BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7; |
116 | writel_relaxed(val, gpc_base + reg); |
117 | } |
118 | |
119 | imx_gpcv2_set_m_core_pgc(enable: false, GPC_PGC_C1); |
120 | } |
121 | |
122 | void imx_enable_cpu(int cpu, bool enable) |
123 | { |
124 | u32 mask, val; |
125 | |
126 | cpu = cpu_logical_map(cpu); |
127 | spin_lock(lock: &scr_lock); |
128 | if (gpr_v2) { |
129 | if (enable) |
130 | imx_gpcv2_set_core1_pdn_pup_by_software(pdn: false); |
131 | |
132 | mask = 1 << (BP_SRC_A7RCR1_A7_CORE1_ENABLE + cpu - 1); |
133 | val = readl_relaxed(src_base + SRC_A7RCR1); |
134 | val = enable ? val | mask : val & ~mask; |
135 | writel_relaxed(val, src_base + SRC_A7RCR1); |
136 | } else { |
137 | mask = 1 << (BP_SRC_SCR_CORE1_ENABLE + cpu - 1); |
138 | val = readl_relaxed(src_base + SRC_SCR); |
139 | val = enable ? val | mask : val & ~mask; |
140 | val |= 1 << (BP_SRC_SCR_CORE1_RST + cpu - 1); |
141 | writel_relaxed(val, src_base + SRC_SCR); |
142 | } |
143 | spin_unlock(lock: &scr_lock); |
144 | } |
145 | |
146 | void imx_set_cpu_jump(int cpu, void *jump_addr) |
147 | { |
148 | cpu = cpu_logical_map(cpu); |
149 | writel_relaxed(__pa_symbol(jump_addr), |
150 | src_base + SRC_GPR1(gpr_v2) + cpu * 8); |
151 | } |
152 | |
153 | u32 imx_get_cpu_arg(int cpu) |
154 | { |
155 | cpu = cpu_logical_map(cpu); |
156 | return readl_relaxed(src_base + SRC_GPR1(gpr_v2) + cpu * 8 + 4); |
157 | } |
158 | |
159 | void imx_set_cpu_arg(int cpu, u32 arg) |
160 | { |
161 | cpu = cpu_logical_map(cpu); |
162 | writel_relaxed(arg, src_base + SRC_GPR1(gpr_v2) + cpu * 8 + 4); |
163 | } |
164 | |
165 | void __init imx_src_init(void) |
166 | { |
167 | struct device_node *np; |
168 | u32 val; |
169 | |
170 | np = of_find_compatible_node(NULL, NULL, compat: "fsl,imx51-src" ); |
171 | if (!np) |
172 | return; |
173 | src_base = of_iomap(node: np, index: 0); |
174 | WARN_ON(!src_base); |
175 | |
176 | /* |
177 | * force warm reset sources to generate cold reset |
178 | * for a more reliable restart |
179 | */ |
180 | spin_lock(lock: &scr_lock); |
181 | val = readl_relaxed(src_base + SRC_SCR); |
182 | val &= ~(1 << BP_SRC_SCR_WARM_RESET_ENABLE); |
183 | writel_relaxed(val, src_base + SRC_SCR); |
184 | spin_unlock(lock: &scr_lock); |
185 | } |
186 | |
187 | void __init imx7_src_init(void) |
188 | { |
189 | struct device_node *np; |
190 | |
191 | gpr_v2 = true; |
192 | |
193 | np = of_find_compatible_node(NULL, NULL, compat: "fsl,imx7d-src" ); |
194 | if (!np) |
195 | return; |
196 | |
197 | src_base = of_iomap(node: np, index: 0); |
198 | if (!src_base) |
199 | return; |
200 | |
201 | np = of_find_compatible_node(NULL, NULL, compat: "fsl,imx7d-gpc" ); |
202 | if (!np) |
203 | return; |
204 | |
205 | gpc_base = of_iomap(node: np, index: 0); |
206 | if (!gpc_base) |
207 | return; |
208 | } |
209 | |
210 | static const struct of_device_id imx_src_dt_ids[] = { |
211 | { .compatible = "fsl,imx51-src" }, |
212 | { /* sentinel */ } |
213 | }; |
214 | |
215 | static int imx_src_probe(struct platform_device *pdev) |
216 | { |
217 | struct reset_controller_dev *rcdev; |
218 | |
219 | rcdev = devm_kzalloc(dev: &pdev->dev, size: sizeof(*rcdev), GFP_KERNEL); |
220 | if (!rcdev) |
221 | return -ENOMEM; |
222 | |
223 | rcdev->ops = &imx_src_ops; |
224 | rcdev->dev = &pdev->dev; |
225 | rcdev->of_node = pdev->dev.of_node; |
226 | rcdev->nr_resets = ARRAY_SIZE(sw_reset_bits); |
227 | |
228 | return devm_reset_controller_register(dev: &pdev->dev, rcdev); |
229 | } |
230 | |
231 | static struct platform_driver imx_src_driver = { |
232 | .driver = { |
233 | .name = "imx-src" , |
234 | .of_match_table = imx_src_dt_ids, |
235 | }, |
236 | .probe = imx_src_probe, |
237 | }; |
238 | builtin_platform_driver(imx_src_driver); |
239 | |