summaryrefslogtreecommitdiff
path: root/drivers/soc/renesas/rcar-rst.c
blob: 98fd97da6cd4330dcd1673a2f7d775937e80e174 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
81
82
83
84
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// SPDX-License-Identifier: GPL-2.0
/*
 * R-Car Gen1 RESET/WDT, R-Car Gen2, Gen3, and RZ/G RST Driver
 *
 * Copyright (C) 2016 Glider bvba
 */

#include <linux/err.h>
#include <linux/io.h>
#include <linux/of_address.h>
#include <linux/soc/renesas/rcar-rst.h>

#define WDTRSTCR_RESET		0xA55A0002
#define WDTRSTCR		0x0054
#define GEN4_WDTRSTCR		0x0010

#define CR7BAR			0x0070
#define CR7BAREN		BIT(4)
#define CR7BAR_MASK		0xFFFC0000

static void __iomem *rcar_rst_base;
static u32 saved_mode __initdata;
static int (*rcar_rst_set_rproc_boot_addr_func)(u64 boot_addr);

static int rcar_rst_enable_wdt_reset(void __iomem *base)
{
	iowrite32(WDTRSTCR_RESET, base + WDTRSTCR);
	return 0;
}

static int rcar_rst_v3u_enable_wdt_reset(void __iomem *base)
{
	iowrite32(WDTRSTCR_RESET, base + GEN4_WDTRSTCR);
	return 0;
}

/*
 * Most of the R-Car Gen3 SoCs have an ARM Realtime Core.
 * Firmware boot address has to be set in CR7BAR before
 * starting the realtime core.
 * Boot address must be aligned on a 256k boundary.
 */
static int rcar_rst_set_gen3_rproc_boot_addr(u64 boot_addr)
{
	if (boot_addr & ~(u64)CR7BAR_MASK) {
		pr_err("Invalid boot address got %llx\n", boot_addr);
		return -EINVAL;
	}

	iowrite32(boot_addr, rcar_rst_base + CR7BAR);
	iowrite32(boot_addr | CR7BAREN, rcar_rst_base + CR7BAR);

	return 0;
}

struct rst_config {
	unsigned int modemr;		/* Mode Monitoring Register Offset */
	int (*configure)(void __iomem *base);	/* Platform specific config */
	int (*set_rproc_boot_addr)(u64 boot_addr);
};

static const struct rst_config rcar_rst_gen1 __initconst = {
	.modemr = 0x20,
};

static const struct rst_config rcar_rst_gen2 __initconst = {
	.modemr = 0x60,
	.configure = rcar_rst_enable_wdt_reset,
};

static const struct rst_config rcar_rst_gen3 __initconst = {
	.modemr = 0x60,
	.set_rproc_boot_addr = rcar_rst_set_gen3_rproc_boot_addr,
};

/* V3U firmware doesn't enable WDT reset and there won't be updates anymore */
static const struct rst_config rcar_rst_v3u __initconst = {
	.modemr = 0x00,		/* MODEMR0 and it has CPG related bits */
	.configure = rcar_rst_v3u_enable_wdt_reset,
};

static const struct rst_config rcar_rst_gen4 __initconst = {
	.modemr = 0x00,		/* MODEMR0 and it has CPG related bits */
};

static const struct of_device_id rcar_rst_matches[] __initconst = {
	/* RZ/G1 is handled like R-Car Gen2 */
	{ .compatible = "renesas,r8a7742-rst", .data = &rcar_rst_gen2 },
	{ .compatible = "renesas,r8a7743-rst", .data = &rcar_rst_gen2 },
	{ .compatible = "renesas,r8a7744-rst", .data = &rcar_rst_gen2 },
	{ .compatible = "renesas,r8a7745-rst", .data = &rcar_rst_gen2 },
	{ .compatible = "renesas,r8a77470-rst", .data = &rcar_rst_gen2 },
	/* RZ/G2 is handled like R-Car Gen3 */
	{ .compatible = "renesas,r8a774a1-rst", .data = &rcar_rst_gen3 },
	{ .compatible = "renesas,r8a774b1-rst", .data = &rcar_rst_gen3 },
	{ .compatible = "renesas,r8a774c0-rst", .data = &rcar_rst_gen3 },
	{ .compatible = "renesas,r8a774e1-rst", .data = &rcar_rst_gen3 },
	/* R-Car Gen1 */
	{ .compatible = "renesas,r8a7778-reset-wdt", .data = &rcar_rst_gen1 },
	{ .compatible = "renesas,r8a7779-reset-wdt", .data = &rcar_rst_gen1 },
	/* R-Car Gen2 */
	{ .compatible = "renesas,r8a7790-rst", .data = &rcar_rst_gen2 },
	{ .compatible = "renesas,r8a7791-rst", .data = &rcar_rst_gen2 },
	{ .compatible = "renesas,r8a7792-rst", .data = &rcar_rst_gen2 },
	{ .compatible = "renesas,r8a7793-rst", .data = &rcar_rst_gen2 },
	{ .compatible = "renesas,r8a7794-rst", .data = &rcar_rst_gen2 },
	/* R-Car Gen3 */
	{ .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen3 },
	{ .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen3 },
	{ .compatible = "renesas,r8a77961-rst", .data = &rcar_rst_gen3 },
	{ .compatible = "renesas,r8a77965-rst", .data = &rcar_rst_gen3 },
	{ .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen3 },
	{ .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 },
	{ .compatible = "renesas,r8a77990-rst", .data = &rcar_rst_gen3 },
	{ .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen3 },
	/* R-Car Gen4 */
	{ .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_v3u },
	{ .compatible = "renesas,r8a779f0-rst", .data = &rcar_rst_gen4 },
	{ .compatible = "renesas,r8a779g0-rst", .data = &rcar_rst_gen4 },
	{ /* sentinel */ }
};

static int __init rcar_rst_init(void)
{
	const struct of_device_id *match;
	const struct rst_config *cfg;
	struct device_node *np;
	void __iomem *base;
	int error = 0;

	np = of_find_matching_node_and_match(NULL, rcar_rst_matches, &match);
	if (!np)
		return -ENODEV;

	base = of_iomap(np, 0);
	if (!base) {
		pr_warn("%pOF: Cannot map regs\n", np);
		error = -ENOMEM;
		goto out_put;
	}

	rcar_rst_base = base;
	cfg = match->data;
	rcar_rst_set_rproc_boot_addr_func = cfg->set_rproc_boot_addr;

	saved_mode = ioread32(base + cfg->modemr);
	if (cfg->configure) {
		error = cfg->configure(base);
		if (error) {
			pr_warn("%pOF: Cannot run SoC specific configuration\n",
				np);
			goto out_put;
		}
	}

	pr_debug("%pOF: MODE = 0x%08x\n", np, saved_mode);

out_put:
	of_node_put(np);
	return error;
}

int __init rcar_rst_read_mode_pins(u32 *mode)
{
	int error;

	if (!rcar_rst_base) {
		error = rcar_rst_init();
		if (error)
			return error;
	}

	*mode = saved_mode;
	return 0;
}

int rcar_rst_set_rproc_boot_addr(u64 boot_addr)
{
	if (!rcar_rst_set_rproc_boot_addr_func)
		return -EIO;

	return rcar_rst_set_rproc_boot_addr_func(boot_addr);
}
EXPORT_SYMBOL_GPL(rcar_rst_set_rproc_boot_addr);