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