162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * R-Car Gen1 RESET/WDT, R-Car Gen2, Gen3, and RZ/G RST Driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2016 Glider bvba
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci#include <linux/io.h>
1062306a36Sopenharmony_ci#include <linux/of_address.h>
1162306a36Sopenharmony_ci#include <linux/soc/renesas/rcar-rst.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define WDTRSTCR_RESET		0xA55A0002
1462306a36Sopenharmony_ci#define WDTRSTCR		0x0054
1562306a36Sopenharmony_ci#define GEN4_WDTRSTCR		0x0010
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define CR7BAR			0x0070
1862306a36Sopenharmony_ci#define CR7BAREN		BIT(4)
1962306a36Sopenharmony_ci#define CR7BAR_MASK		0xFFFC0000
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic void __iomem *rcar_rst_base;
2262306a36Sopenharmony_cistatic u32 saved_mode __initdata;
2362306a36Sopenharmony_cistatic int (*rcar_rst_set_rproc_boot_addr_func)(u64 boot_addr);
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int rcar_rst_enable_wdt_reset(void __iomem *base)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	iowrite32(WDTRSTCR_RESET, base + WDTRSTCR);
2862306a36Sopenharmony_ci	return 0;
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic int rcar_rst_v3u_enable_wdt_reset(void __iomem *base)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	iowrite32(WDTRSTCR_RESET, base + GEN4_WDTRSTCR);
3462306a36Sopenharmony_ci	return 0;
3562306a36Sopenharmony_ci}
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci/*
3862306a36Sopenharmony_ci * Most of the R-Car Gen3 SoCs have an ARM Realtime Core.
3962306a36Sopenharmony_ci * Firmware boot address has to be set in CR7BAR before
4062306a36Sopenharmony_ci * starting the realtime core.
4162306a36Sopenharmony_ci * Boot address must be aligned on a 256k boundary.
4262306a36Sopenharmony_ci */
4362306a36Sopenharmony_cistatic int rcar_rst_set_gen3_rproc_boot_addr(u64 boot_addr)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	if (boot_addr & ~(u64)CR7BAR_MASK) {
4662306a36Sopenharmony_ci		pr_err("Invalid boot address got %llx\n", boot_addr);
4762306a36Sopenharmony_ci		return -EINVAL;
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	iowrite32(boot_addr, rcar_rst_base + CR7BAR);
5162306a36Sopenharmony_ci	iowrite32(boot_addr | CR7BAREN, rcar_rst_base + CR7BAR);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	return 0;
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistruct rst_config {
5762306a36Sopenharmony_ci	unsigned int modemr;		/* Mode Monitoring Register Offset */
5862306a36Sopenharmony_ci	int (*configure)(void __iomem *base);	/* Platform specific config */
5962306a36Sopenharmony_ci	int (*set_rproc_boot_addr)(u64 boot_addr);
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic const struct rst_config rcar_rst_gen1 __initconst = {
6362306a36Sopenharmony_ci	.modemr = 0x20,
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic const struct rst_config rcar_rst_gen2 __initconst = {
6762306a36Sopenharmony_ci	.modemr = 0x60,
6862306a36Sopenharmony_ci	.configure = rcar_rst_enable_wdt_reset,
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic const struct rst_config rcar_rst_gen3 __initconst = {
7262306a36Sopenharmony_ci	.modemr = 0x60,
7362306a36Sopenharmony_ci	.set_rproc_boot_addr = rcar_rst_set_gen3_rproc_boot_addr,
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci/* V3U firmware doesn't enable WDT reset and there won't be updates anymore */
7762306a36Sopenharmony_cistatic const struct rst_config rcar_rst_v3u __initconst = {
7862306a36Sopenharmony_ci	.modemr = 0x00,		/* MODEMR0 and it has CPG related bits */
7962306a36Sopenharmony_ci	.configure = rcar_rst_v3u_enable_wdt_reset,
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic const struct rst_config rcar_rst_gen4 __initconst = {
8362306a36Sopenharmony_ci	.modemr = 0x00,		/* MODEMR0 and it has CPG related bits */
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic const struct of_device_id rcar_rst_matches[] __initconst = {
8762306a36Sopenharmony_ci	/* RZ/G1 is handled like R-Car Gen2 */
8862306a36Sopenharmony_ci	{ .compatible = "renesas,r8a7742-rst", .data = &rcar_rst_gen2 },
8962306a36Sopenharmony_ci	{ .compatible = "renesas,r8a7743-rst", .data = &rcar_rst_gen2 },
9062306a36Sopenharmony_ci	{ .compatible = "renesas,r8a7744-rst", .data = &rcar_rst_gen2 },
9162306a36Sopenharmony_ci	{ .compatible = "renesas,r8a7745-rst", .data = &rcar_rst_gen2 },
9262306a36Sopenharmony_ci	{ .compatible = "renesas,r8a77470-rst", .data = &rcar_rst_gen2 },
9362306a36Sopenharmony_ci	/* RZ/G2 is handled like R-Car Gen3 */
9462306a36Sopenharmony_ci	{ .compatible = "renesas,r8a774a1-rst", .data = &rcar_rst_gen3 },
9562306a36Sopenharmony_ci	{ .compatible = "renesas,r8a774b1-rst", .data = &rcar_rst_gen3 },
9662306a36Sopenharmony_ci	{ .compatible = "renesas,r8a774c0-rst", .data = &rcar_rst_gen3 },
9762306a36Sopenharmony_ci	{ .compatible = "renesas,r8a774e1-rst", .data = &rcar_rst_gen3 },
9862306a36Sopenharmony_ci	/* R-Car Gen1 */
9962306a36Sopenharmony_ci	{ .compatible = "renesas,r8a7778-reset-wdt", .data = &rcar_rst_gen1 },
10062306a36Sopenharmony_ci	{ .compatible = "renesas,r8a7779-reset-wdt", .data = &rcar_rst_gen1 },
10162306a36Sopenharmony_ci	/* R-Car Gen2 */
10262306a36Sopenharmony_ci	{ .compatible = "renesas,r8a7790-rst", .data = &rcar_rst_gen2 },
10362306a36Sopenharmony_ci	{ .compatible = "renesas,r8a7791-rst", .data = &rcar_rst_gen2 },
10462306a36Sopenharmony_ci	{ .compatible = "renesas,r8a7792-rst", .data = &rcar_rst_gen2 },
10562306a36Sopenharmony_ci	{ .compatible = "renesas,r8a7793-rst", .data = &rcar_rst_gen2 },
10662306a36Sopenharmony_ci	{ .compatible = "renesas,r8a7794-rst", .data = &rcar_rst_gen2 },
10762306a36Sopenharmony_ci	/* R-Car Gen3 */
10862306a36Sopenharmony_ci	{ .compatible = "renesas,r8a7795-rst", .data = &rcar_rst_gen3 },
10962306a36Sopenharmony_ci	{ .compatible = "renesas,r8a7796-rst", .data = &rcar_rst_gen3 },
11062306a36Sopenharmony_ci	{ .compatible = "renesas,r8a77961-rst", .data = &rcar_rst_gen3 },
11162306a36Sopenharmony_ci	{ .compatible = "renesas,r8a77965-rst", .data = &rcar_rst_gen3 },
11262306a36Sopenharmony_ci	{ .compatible = "renesas,r8a77970-rst", .data = &rcar_rst_gen3 },
11362306a36Sopenharmony_ci	{ .compatible = "renesas,r8a77980-rst", .data = &rcar_rst_gen3 },
11462306a36Sopenharmony_ci	{ .compatible = "renesas,r8a77990-rst", .data = &rcar_rst_gen3 },
11562306a36Sopenharmony_ci	{ .compatible = "renesas,r8a77995-rst", .data = &rcar_rst_gen3 },
11662306a36Sopenharmony_ci	/* R-Car Gen4 */
11762306a36Sopenharmony_ci	{ .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_v3u },
11862306a36Sopenharmony_ci	{ .compatible = "renesas,r8a779f0-rst", .data = &rcar_rst_gen4 },
11962306a36Sopenharmony_ci	{ .compatible = "renesas,r8a779g0-rst", .data = &rcar_rst_gen4 },
12062306a36Sopenharmony_ci	{ /* sentinel */ }
12162306a36Sopenharmony_ci};
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int __init rcar_rst_init(void)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	const struct of_device_id *match;
12662306a36Sopenharmony_ci	const struct rst_config *cfg;
12762306a36Sopenharmony_ci	struct device_node *np;
12862306a36Sopenharmony_ci	void __iomem *base;
12962306a36Sopenharmony_ci	int error = 0;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	np = of_find_matching_node_and_match(NULL, rcar_rst_matches, &match);
13262306a36Sopenharmony_ci	if (!np)
13362306a36Sopenharmony_ci		return -ENODEV;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	base = of_iomap(np, 0);
13662306a36Sopenharmony_ci	if (!base) {
13762306a36Sopenharmony_ci		pr_warn("%pOF: Cannot map regs\n", np);
13862306a36Sopenharmony_ci		error = -ENOMEM;
13962306a36Sopenharmony_ci		goto out_put;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	rcar_rst_base = base;
14362306a36Sopenharmony_ci	cfg = match->data;
14462306a36Sopenharmony_ci	rcar_rst_set_rproc_boot_addr_func = cfg->set_rproc_boot_addr;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	saved_mode = ioread32(base + cfg->modemr);
14762306a36Sopenharmony_ci	if (cfg->configure) {
14862306a36Sopenharmony_ci		error = cfg->configure(base);
14962306a36Sopenharmony_ci		if (error) {
15062306a36Sopenharmony_ci			pr_warn("%pOF: Cannot run SoC specific configuration\n",
15162306a36Sopenharmony_ci				np);
15262306a36Sopenharmony_ci			goto out_put;
15362306a36Sopenharmony_ci		}
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	pr_debug("%pOF: MODE = 0x%08x\n", np, saved_mode);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ciout_put:
15962306a36Sopenharmony_ci	of_node_put(np);
16062306a36Sopenharmony_ci	return error;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ciint __init rcar_rst_read_mode_pins(u32 *mode)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	int error;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (!rcar_rst_base) {
16862306a36Sopenharmony_ci		error = rcar_rst_init();
16962306a36Sopenharmony_ci		if (error)
17062306a36Sopenharmony_ci			return error;
17162306a36Sopenharmony_ci	}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	*mode = saved_mode;
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ciint rcar_rst_set_rproc_boot_addr(u64 boot_addr)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	if (!rcar_rst_set_rproc_boot_addr_func)
18062306a36Sopenharmony_ci		return -EIO;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return rcar_rst_set_rproc_boot_addr_func(boot_addr);
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rcar_rst_set_rproc_boot_addr);
185