162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Xilinx SLCR driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2011-2013 Xilinx Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/reboot.h> 1062306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1162306a36Sopenharmony_ci#include <linux/of_address.h> 1262306a36Sopenharmony_ci#include <linux/regmap.h> 1362306a36Sopenharmony_ci#include <linux/clk/zynq.h> 1462306a36Sopenharmony_ci#include "common.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* register offsets */ 1762306a36Sopenharmony_ci#define SLCR_UNLOCK_OFFSET 0x8 /* SCLR unlock register */ 1862306a36Sopenharmony_ci#define SLCR_PS_RST_CTRL_OFFSET 0x200 /* PS Software Reset Control */ 1962306a36Sopenharmony_ci#define SLCR_A9_CPU_RST_CTRL_OFFSET 0x244 /* CPU Software Reset Control */ 2062306a36Sopenharmony_ci#define SLCR_REBOOT_STATUS_OFFSET 0x258 /* PS Reboot Status */ 2162306a36Sopenharmony_ci#define SLCR_PSS_IDCODE 0x530 /* PS IDCODE */ 2262306a36Sopenharmony_ci#define SLCR_L2C_RAM 0xA1C /* L2C_RAM in AR#54190 */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define SLCR_UNLOCK_MAGIC 0xDF0D 2562306a36Sopenharmony_ci#define SLCR_A9_CPU_CLKSTOP 0x10 2662306a36Sopenharmony_ci#define SLCR_A9_CPU_RST 0x1 2762306a36Sopenharmony_ci#define SLCR_PSS_IDCODE_DEVICE_SHIFT 12 2862306a36Sopenharmony_ci#define SLCR_PSS_IDCODE_DEVICE_MASK 0x1F 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic void __iomem *zynq_slcr_base; 3162306a36Sopenharmony_cistatic struct regmap *zynq_slcr_regmap; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/** 3462306a36Sopenharmony_ci * zynq_slcr_write - Write to a register in SLCR block 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * @val: Value to write to the register 3762306a36Sopenharmony_ci * @offset: Register offset in SLCR block 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * Return: a negative value on error, 0 on success 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_cistatic int zynq_slcr_write(u32 val, u32 offset) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci return regmap_write(zynq_slcr_regmap, offset, val); 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci/** 4762306a36Sopenharmony_ci * zynq_slcr_read - Read a register in SLCR block 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * @val: Pointer to value to be read from SLCR 5062306a36Sopenharmony_ci * @offset: Register offset in SLCR block 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * Return: a negative value on error, 0 on success 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_cistatic int zynq_slcr_read(u32 *val, u32 offset) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci return regmap_read(zynq_slcr_regmap, offset, val); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/** 6062306a36Sopenharmony_ci * zynq_slcr_unlock - Unlock SLCR registers 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * Return: a negative value on error, 0 on success 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic inline int zynq_slcr_unlock(void) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci zynq_slcr_write(SLCR_UNLOCK_MAGIC, SLCR_UNLOCK_OFFSET); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/** 7262306a36Sopenharmony_ci * zynq_slcr_get_device_id - Read device code id 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * Return: Device code id 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ciu32 zynq_slcr_get_device_id(void) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci u32 val; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci zynq_slcr_read(&val, SLCR_PSS_IDCODE); 8162306a36Sopenharmony_ci val >>= SLCR_PSS_IDCODE_DEVICE_SHIFT; 8262306a36Sopenharmony_ci val &= SLCR_PSS_IDCODE_DEVICE_MASK; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return val; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/** 8862306a36Sopenharmony_ci * zynq_slcr_system_restart - Restart the entire system. 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * @nb: Pointer to restart notifier block (unused) 9162306a36Sopenharmony_ci * @action: Reboot mode (unused) 9262306a36Sopenharmony_ci * @data: Restart handler private data (unused) 9362306a36Sopenharmony_ci * 9462306a36Sopenharmony_ci * Return: 0 always 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_cistatic 9762306a36Sopenharmony_ciint zynq_slcr_system_restart(struct notifier_block *nb, 9862306a36Sopenharmony_ci unsigned long action, void *data) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci u32 reboot; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* 10362306a36Sopenharmony_ci * Clear 0x0F000000 bits of reboot status register to workaround 10462306a36Sopenharmony_ci * the FSBL not loading the bitstream after soft-reboot 10562306a36Sopenharmony_ci * This is a temporary solution until we know more. 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci zynq_slcr_read(&reboot, SLCR_REBOOT_STATUS_OFFSET); 10862306a36Sopenharmony_ci zynq_slcr_write(reboot & 0xF0FFFFFF, SLCR_REBOOT_STATUS_OFFSET); 10962306a36Sopenharmony_ci zynq_slcr_write(1, SLCR_PS_RST_CTRL_OFFSET); 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic struct notifier_block zynq_slcr_restart_nb = { 11462306a36Sopenharmony_ci .notifier_call = zynq_slcr_system_restart, 11562306a36Sopenharmony_ci .priority = 192, 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/** 11962306a36Sopenharmony_ci * zynq_slcr_cpu_start - Start cpu 12062306a36Sopenharmony_ci * @cpu: cpu number 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_civoid zynq_slcr_cpu_start(int cpu) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci u32 reg; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci zynq_slcr_read(®, SLCR_A9_CPU_RST_CTRL_OFFSET); 12762306a36Sopenharmony_ci reg &= ~(SLCR_A9_CPU_RST << cpu); 12862306a36Sopenharmony_ci zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET); 12962306a36Sopenharmony_ci reg &= ~(SLCR_A9_CPU_CLKSTOP << cpu); 13062306a36Sopenharmony_ci zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci zynq_slcr_cpu_state_write(cpu, false); 13362306a36Sopenharmony_ci} 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/** 13662306a36Sopenharmony_ci * zynq_slcr_cpu_stop - Stop cpu 13762306a36Sopenharmony_ci * @cpu: cpu number 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_civoid zynq_slcr_cpu_stop(int cpu) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci u32 reg; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci zynq_slcr_read(®, SLCR_A9_CPU_RST_CTRL_OFFSET); 14462306a36Sopenharmony_ci reg |= (SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu; 14562306a36Sopenharmony_ci zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci/** 14962306a36Sopenharmony_ci * zynq_slcr_cpu_state - Read/write cpu state 15062306a36Sopenharmony_ci * @cpu: cpu number 15162306a36Sopenharmony_ci * 15262306a36Sopenharmony_ci * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1) 15362306a36Sopenharmony_ci * 0 means cpu is running, 1 cpu is going to die. 15462306a36Sopenharmony_ci * 15562306a36Sopenharmony_ci * Return: true if cpu is running, false if cpu is going to die 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_cibool zynq_slcr_cpu_state_read(int cpu) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci u32 state; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET); 16262306a36Sopenharmony_ci state &= 1 << (31 - cpu); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return !state; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/** 16862306a36Sopenharmony_ci * zynq_slcr_cpu_state - Read/write cpu state 16962306a36Sopenharmony_ci * @cpu: cpu number 17062306a36Sopenharmony_ci * @die: cpu state - true if cpu is going to die 17162306a36Sopenharmony_ci * 17262306a36Sopenharmony_ci * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1) 17362306a36Sopenharmony_ci * 0 means cpu is running, 1 cpu is going to die. 17462306a36Sopenharmony_ci */ 17562306a36Sopenharmony_civoid zynq_slcr_cpu_state_write(int cpu, bool die) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci u32 state, mask; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET); 18062306a36Sopenharmony_ci mask = 1 << (31 - cpu); 18162306a36Sopenharmony_ci if (die) 18262306a36Sopenharmony_ci state |= mask; 18362306a36Sopenharmony_ci else 18462306a36Sopenharmony_ci state &= ~mask; 18562306a36Sopenharmony_ci writel(state, zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/** 18962306a36Sopenharmony_ci * zynq_early_slcr_init - Early slcr init function 19062306a36Sopenharmony_ci * 19162306a36Sopenharmony_ci * Return: 0 on success, negative errno otherwise. 19262306a36Sopenharmony_ci * 19362306a36Sopenharmony_ci * Called very early during boot from platform code to unlock SLCR. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_ciint __init zynq_early_slcr_init(void) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct device_node *np; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr"); 20062306a36Sopenharmony_ci if (!np) { 20162306a36Sopenharmony_ci pr_err("%s: no slcr node found\n", __func__); 20262306a36Sopenharmony_ci BUG(); 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci zynq_slcr_base = of_iomap(np, 0); 20662306a36Sopenharmony_ci if (!zynq_slcr_base) { 20762306a36Sopenharmony_ci pr_err("%s: Unable to map I/O memory\n", __func__); 20862306a36Sopenharmony_ci BUG(); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci np->data = (__force void *)zynq_slcr_base; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci zynq_slcr_regmap = syscon_regmap_lookup_by_compatible("xlnx,zynq-slcr"); 21462306a36Sopenharmony_ci if (IS_ERR(zynq_slcr_regmap)) { 21562306a36Sopenharmony_ci pr_err("%s: failed to find zynq-slcr\n", __func__); 21662306a36Sopenharmony_ci of_node_put(np); 21762306a36Sopenharmony_ci return -ENODEV; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* unlock the SLCR so that registers can be changed */ 22162306a36Sopenharmony_ci zynq_slcr_unlock(); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* See AR#54190 design advisory */ 22462306a36Sopenharmony_ci regmap_update_bits(zynq_slcr_regmap, SLCR_L2C_RAM, 0x70707, 0x20202); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci register_restart_handler(&zynq_slcr_restart_nb); 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci pr_info("%pOFn mapped to %p\n", np, zynq_slcr_base); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci of_node_put(np); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci return 0; 23362306a36Sopenharmony_ci} 234