src.c 5.82 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
3
4
5
6
7
8
/*
 * Copyright 2011 Freescale Semiconductor, Inc.
 * Copyright 2011 Linaro Ltd.
 */

#include <linux/init.h>
#include <linux/io.h>
9
#include <linux/iopoll.h>
10
11
#include <linux/of.h>
#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
21
22
#define SRC_GPR1_V1			0x020
#define SRC_GPR1_V2			0x074
#define SRC_GPR1(gpr_v2)		((gpr_v2) ? SRC_GPR1_V2 : SRC_GPR1_V1)
23
#define BP_SRC_SCR_WARM_RESET_ENABLE	0
24
25
26
27
28
#define BP_SRC_SCR_SW_GPU_RST		1
#define BP_SRC_SCR_SW_VPU_RST		2
#define BP_SRC_SCR_SW_IPU1_RST		3
#define BP_SRC_SCR_SW_OPEN_VG_RST	4
#define BP_SRC_SCR_SW_IPU2_RST		12
29
30
#define BP_SRC_SCR_CORE1_RST		14
#define BP_SRC_SCR_CORE1_ENABLE		22
31
32
33
34
35
36
37
/* below is for i.MX7D */
#define SRC_A7RCR1			0x008
#define BP_SRC_A7RCR1_A7_CORE1_ENABLE	1
#define GPC_CPU_PGC_SW_PUP_REQ		0xf0
#define GPC_CPU_PGC_SW_PDN_REQ		0xfc
#define GPC_PGC_C1			0x840
#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
42
static bool gpr_v2;
static void __iomem *gpc_base;
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

static const int sw_reset_bits[5] = {
	BP_SRC_SCR_SW_GPU_RST,
	BP_SRC_SCR_SW_VPU_RST,
	BP_SRC_SCR_SW_IPU1_RST,
	BP_SRC_SCR_SW_OPEN_VG_RST,
	BP_SRC_SCR_SW_IPU2_RST
};

static int imx_src_reset_module(struct reset_controller_dev *rcdev,
		unsigned long sw_reset_idx)
{
	unsigned long timeout;
	unsigned long flags;
	int bit;
	u32 val;

	if (sw_reset_idx >= ARRAY_SIZE(sw_reset_bits))
		return -EINVAL;

	bit = 1 << sw_reset_bits[sw_reset_idx];

	spin_lock_irqsave(&scr_lock, flags);
	val = readl_relaxed(src_base + SRC_SCR);
	val |= bit;
	writel_relaxed(val, src_base + SRC_SCR);
	spin_unlock_irqrestore(&scr_lock, flags);

	timeout = jiffies + msecs_to_jiffies(1000);
	while (readl(src_base + SRC_SCR) & bit) {
		if (time_after(jiffies, timeout))
			return -ETIME;
		cpu_relax();
	}

	return 0;
}

81
static const struct reset_control_ops imx_src_ops = {
82
83
84
	.reset = imx_src_reset_module,
};

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
static void imx_gpcv2_set_m_core_pgc(bool enable, u32 offset)
{
	writel_relaxed(enable, gpc_base + offset);
}

/*
 * The motivation for bringing up the second i.MX7D core inside the kernel
 * is that legacy vendor bootloaders usually do not implement PSCI support.
 * This is a significant blocker for systems in the field that are running old
 * bootloader versions to upgrade to a modern mainline kernel version, as only
 * one CPU of the i.MX7D would be brought up.
 * Bring up the second i.MX7D core inside the kernel to make the migration
 * path to mainline kernel easier for the existing iMX7D users.
 */
void imx_gpcv2_set_core1_pdn_pup_by_software(bool pdn)
{
	u32 reg = pdn ? GPC_CPU_PGC_SW_PDN_REQ : GPC_CPU_PGC_SW_PUP_REQ;
	u32 val, pup;
	int ret;

	imx_gpcv2_set_m_core_pgc(true, GPC_PGC_C1);
	val = readl_relaxed(gpc_base + reg);
	val |= BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7;
	writel_relaxed(val, gpc_base + reg);

	ret = readl_relaxed_poll_timeout_atomic(gpc_base + reg, pup,
				!(pup & BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7),
				5, 1000000);
	if (ret < 0) {
		pr_err("i.MX7D: CORE1_A7 power up timeout\n");
		val &= ~BM_CPU_PGC_SW_PDN_PUP_REQ_CORE1_A7;
		writel_relaxed(val, gpc_base + reg);
	}

	imx_gpcv2_set_m_core_pgc(false, GPC_PGC_C1);
}

122
123
124
125
void imx_enable_cpu(int cpu, bool enable)
{
	u32 mask, val;

126
	cpu = cpu_logical_map(cpu);
127
	spin_lock(&scr_lock);
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
	if (gpr_v2) {
		if (enable)
			imx_gpcv2_set_core1_pdn_pup_by_software(false);

		mask = 1 << (BP_SRC_A7RCR1_A7_CORE1_ENABLE + cpu - 1);
		val = readl_relaxed(src_base + SRC_A7RCR1);
		val = enable ? val | mask : val & ~mask;
		writel_relaxed(val, src_base + SRC_A7RCR1);
	} else {
		mask = 1 << (BP_SRC_SCR_CORE1_ENABLE + cpu - 1);
		val = readl_relaxed(src_base + SRC_SCR);
		val = enable ? val | mask : val & ~mask;
		val |= 1 << (BP_SRC_SCR_CORE1_RST + cpu - 1);
		writel_relaxed(val, src_base + SRC_SCR);
	}
143
	spin_unlock(&scr_lock);
144
145
146
147
}

void imx_set_cpu_jump(int cpu, void *jump_addr)
{
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
154
155
u32 imx_get_cpu_arg(int cpu)
{
	cpu = cpu_logical_map(cpu);
156
	return readl_relaxed(src_base + SRC_GPR1(gpr_v2) + cpu * 8 + 4);
157
158
159
160
161
}

void imx_set_cpu_arg(int cpu, u32 arg)
{
	cpu = cpu_logical_map(cpu);
162
	writel_relaxed(arg, src_base + SRC_GPR1(gpr_v2) + cpu * 8 + 4);
163
164
}

165
166
167
void __init imx_src_init(void)
{
	struct device_node *np;
168
	u32 val;
169

170
171
172
	np = of_find_compatible_node(NULL, NULL, "fsl,imx51-src");
	if (!np)
		return;
173
174
	src_base = of_iomap(np, 0);
	WARN_ON(!src_base);
175
176
177
178
179

	/*
	 * force warm reset sources to generate cold reset
	 * for a more reliable restart
	 */
180
	spin_lock(&scr_lock);
181
182
183
	val = readl_relaxed(src_base + SRC_SCR);
	val &= ~(1 << BP_SRC_SCR_WARM_RESET_ENABLE);
	writel_relaxed(val, src_base + SRC_SCR);
184
	spin_unlock(&scr_lock);
185
}
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

void __init imx7_src_init(void)
{
	struct device_node *np;

	gpr_v2 = true;

	np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-src");
	if (!np)
		return;

	src_base = of_iomap(np, 0);
	if (!src_base)
		return;

	np = of_find_compatible_node(NULL, NULL, "fsl,imx7d-gpc");
	if (!np)
		return;

	gpc_base = of_iomap(np, 0);
	if (!gpc_base)
		return;
}
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238

static const struct of_device_id imx_src_dt_ids[] = {
	{ .compatible = "fsl,imx51-src" },
	{ /* sentinel */ }
};

static int imx_src_probe(struct platform_device *pdev)
{
	struct reset_controller_dev *rcdev;

	rcdev = devm_kzalloc(&pdev->dev, sizeof(*rcdev), GFP_KERNEL);
	if (!rcdev)
		return -ENOMEM;

	rcdev->ops = &imx_src_ops;
	rcdev->dev = &pdev->dev;
	rcdev->of_node = pdev->dev.of_node;
	rcdev->nr_resets = ARRAY_SIZE(sw_reset_bits);

	return devm_reset_controller_register(&pdev->dev, rcdev);
}

static struct platform_driver imx_src_driver = {
	.driver = {
		.name = "imx-src",
		.of_match_table = imx_src_dt_ids,
	},
	.probe = imx_src_probe,
};
builtin_platform_driver(imx_src_driver);