xref: /linux/arch/arm/mach-zynq/slcr.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
1a912e80bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
264b889b3SMichal Simek /*
364b889b3SMichal Simek  * Xilinx SLCR driver
464b889b3SMichal Simek  *
564b889b3SMichal Simek  * Copyright (c) 2011-2013 Xilinx Inc.
664b889b3SMichal Simek  */
764b889b3SMichal Simek 
864b889b3SMichal Simek #include <linux/io.h>
964e68617SJosh Cartwright #include <linux/reboot.h>
10016f4dcaSMichal Simek #include <linux/mfd/syscon.h>
1164b889b3SMichal Simek #include <linux/of_address.h>
12016f4dcaSMichal Simek #include <linux/regmap.h>
1364b889b3SMichal Simek #include "common.h"
1464b889b3SMichal Simek 
15b5f177ffSSoren Brinkmann /* register offsets */
16b5f177ffSSoren Brinkmann #define SLCR_UNLOCK_OFFSET		0x8   /* SCLR unlock register */
1796790f0aSMichal Simek #define SLCR_PS_RST_CTRL_OFFSET		0x200 /* PS Software Reset Control */
18b5f177ffSSoren Brinkmann #define SLCR_A9_CPU_RST_CTRL_OFFSET	0x244 /* CPU Software Reset Control */
19b5f177ffSSoren Brinkmann #define SLCR_REBOOT_STATUS_OFFSET	0x258 /* PS Reboot Status */
2000f7dc63SMichal Simek #define SLCR_PSS_IDCODE			0x530 /* PS IDCODE */
216ded93a1SJosh Cartwright #define SLCR_L2C_RAM			0xA1C /* L2C_RAM in AR#54190 */
22aa7eb2bbSMichal Simek 
23b5f177ffSSoren Brinkmann #define SLCR_UNLOCK_MAGIC		0xDF0D
24aa7eb2bbSMichal Simek #define SLCR_A9_CPU_CLKSTOP		0x10
25aa7eb2bbSMichal Simek #define SLCR_A9_CPU_RST			0x1
2600f7dc63SMichal Simek #define SLCR_PSS_IDCODE_DEVICE_SHIFT	12
2700f7dc63SMichal Simek #define SLCR_PSS_IDCODE_DEVICE_MASK	0x1F
28aa7eb2bbSMichal Simek 
297b274efeSSteffen Trumtrar static void __iomem *zynq_slcr_base;
30016f4dcaSMichal Simek static struct regmap *zynq_slcr_regmap;
3164b889b3SMichal Simek 
3264b889b3SMichal Simek /**
33871c6971SMichal Simek  * zynq_slcr_write - Write to a register in SLCR block
34871c6971SMichal Simek  *
35871c6971SMichal Simek  * @val:	Value to write to the register
36871c6971SMichal Simek  * @offset:	Register offset in SLCR block
37871c6971SMichal Simek  *
38871c6971SMichal Simek  * Return:	a negative value on error, 0 on success
39871c6971SMichal Simek  */
zynq_slcr_write(u32 val,u32 offset)40871c6971SMichal Simek static int zynq_slcr_write(u32 val, u32 offset)
41871c6971SMichal Simek {
42871c6971SMichal Simek 	return regmap_write(zynq_slcr_regmap, offset, val);
43871c6971SMichal Simek }
44871c6971SMichal Simek 
45871c6971SMichal Simek /**
46871c6971SMichal Simek  * zynq_slcr_read - Read a register in SLCR block
47871c6971SMichal Simek  *
48871c6971SMichal Simek  * @val:	Pointer to value to be read from SLCR
49871c6971SMichal Simek  * @offset:	Register offset in SLCR block
50871c6971SMichal Simek  *
51871c6971SMichal Simek  * Return:	a negative value on error, 0 on success
52871c6971SMichal Simek  */
zynq_slcr_read(u32 * val,u32 offset)53871c6971SMichal Simek static int zynq_slcr_read(u32 *val, u32 offset)
54871c6971SMichal Simek {
55871c6971SMichal Simek 	return regmap_read(zynq_slcr_regmap, offset, val);
56871c6971SMichal Simek }
57871c6971SMichal Simek 
58871c6971SMichal Simek /**
5956880073SMichal Simek  * zynq_slcr_unlock - Unlock SLCR registers
6056880073SMichal Simek  *
6156880073SMichal Simek  * Return:	a negative value on error, 0 on success
6256880073SMichal Simek  */
zynq_slcr_unlock(void)6356880073SMichal Simek static inline int zynq_slcr_unlock(void)
6456880073SMichal Simek {
6556880073SMichal Simek 	zynq_slcr_write(SLCR_UNLOCK_MAGIC, SLCR_UNLOCK_OFFSET);
6656880073SMichal Simek 
6756880073SMichal Simek 	return 0;
6856880073SMichal Simek }
6956880073SMichal Simek 
7056880073SMichal Simek /**
7100f7dc63SMichal Simek  * zynq_slcr_get_device_id - Read device code id
7200f7dc63SMichal Simek  *
7300f7dc63SMichal Simek  * Return:	Device code id
7400f7dc63SMichal Simek  */
zynq_slcr_get_device_id(void)7500f7dc63SMichal Simek u32 zynq_slcr_get_device_id(void)
7600f7dc63SMichal Simek {
7700f7dc63SMichal Simek 	u32 val;
7800f7dc63SMichal Simek 
7900f7dc63SMichal Simek 	zynq_slcr_read(&val, SLCR_PSS_IDCODE);
8000f7dc63SMichal Simek 	val >>= SLCR_PSS_IDCODE_DEVICE_SHIFT;
8100f7dc63SMichal Simek 	val &= SLCR_PSS_IDCODE_DEVICE_MASK;
8200f7dc63SMichal Simek 
8300f7dc63SMichal Simek 	return val;
8400f7dc63SMichal Simek }
8500f7dc63SMichal Simek 
8600f7dc63SMichal Simek /**
8764e68617SJosh Cartwright  * zynq_slcr_system_restart - Restart the entire system.
8864e68617SJosh Cartwright  *
8964e68617SJosh Cartwright  * @nb:		Pointer to restart notifier block (unused)
9064e68617SJosh Cartwright  * @action:	Reboot mode (unused)
9164e68617SJosh Cartwright  * @data:	Restart handler private data (unused)
9264e68617SJosh Cartwright  *
9364e68617SJosh Cartwright  * Return:	0 always
9496790f0aSMichal Simek  */
9564e68617SJosh Cartwright static
zynq_slcr_system_restart(struct notifier_block * nb,unsigned long action,void * data)9664e68617SJosh Cartwright int zynq_slcr_system_restart(struct notifier_block *nb,
9764e68617SJosh Cartwright 			     unsigned long action, void *data)
9896790f0aSMichal Simek {
9996790f0aSMichal Simek 	u32 reboot;
10096790f0aSMichal Simek 
10196790f0aSMichal Simek 	/*
10296790f0aSMichal Simek 	 * Clear 0x0F000000 bits of reboot status register to workaround
10396790f0aSMichal Simek 	 * the FSBL not loading the bitstream after soft-reboot
10496790f0aSMichal Simek 	 * This is a temporary solution until we know more.
10596790f0aSMichal Simek 	 */
106871c6971SMichal Simek 	zynq_slcr_read(&reboot, SLCR_REBOOT_STATUS_OFFSET);
107871c6971SMichal Simek 	zynq_slcr_write(reboot & 0xF0FFFFFF, SLCR_REBOOT_STATUS_OFFSET);
108871c6971SMichal Simek 	zynq_slcr_write(1, SLCR_PS_RST_CTRL_OFFSET);
10964e68617SJosh Cartwright 	return 0;
11096790f0aSMichal Simek }
11196790f0aSMichal Simek 
11264e68617SJosh Cartwright static struct notifier_block zynq_slcr_restart_nb = {
11364e68617SJosh Cartwright 	.notifier_call	= zynq_slcr_system_restart,
11464e68617SJosh Cartwright 	.priority	= 192,
11564e68617SJosh Cartwright };
11664e68617SJosh Cartwright 
11796790f0aSMichal Simek /**
118aa7eb2bbSMichal Simek  * zynq_slcr_cpu_start - Start cpu
119aa7eb2bbSMichal Simek  * @cpu:	cpu number
120aa7eb2bbSMichal Simek  */
zynq_slcr_cpu_start(int cpu)121aa7eb2bbSMichal Simek void zynq_slcr_cpu_start(int cpu)
122aa7eb2bbSMichal Simek {
123871c6971SMichal Simek 	u32 reg;
124871c6971SMichal Simek 
125871c6971SMichal Simek 	zynq_slcr_read(&reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
1263db9e860SSoren Brinkmann 	reg &= ~(SLCR_A9_CPU_RST << cpu);
127871c6971SMichal Simek 	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
1283db9e860SSoren Brinkmann 	reg &= ~(SLCR_A9_CPU_CLKSTOP << cpu);
129871c6971SMichal Simek 	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
13050c7960aSSoren Brinkmann 
13150c7960aSSoren Brinkmann 	zynq_slcr_cpu_state_write(cpu, false);
132aa7eb2bbSMichal Simek }
133aa7eb2bbSMichal Simek 
134aa7eb2bbSMichal Simek /**
135aa7eb2bbSMichal Simek  * zynq_slcr_cpu_stop - Stop cpu
136aa7eb2bbSMichal Simek  * @cpu:	cpu number
137aa7eb2bbSMichal Simek  */
zynq_slcr_cpu_stop(int cpu)138aa7eb2bbSMichal Simek void zynq_slcr_cpu_stop(int cpu)
139aa7eb2bbSMichal Simek {
140871c6971SMichal Simek 	u32 reg;
141871c6971SMichal Simek 
142871c6971SMichal Simek 	zynq_slcr_read(&reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
1433db9e860SSoren Brinkmann 	reg |= (SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu;
144871c6971SMichal Simek 	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
145aa7eb2bbSMichal Simek }
146aa7eb2bbSMichal Simek 
147aa7eb2bbSMichal Simek /**
148*554add03SRandy Dunlap  * zynq_slcr_cpu_state_read - Read cpu state
14950c7960aSSoren Brinkmann  * @cpu:	cpu number
150016f4dcaSMichal Simek  *
15150c7960aSSoren Brinkmann  * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1)
15250c7960aSSoren Brinkmann  * 0 means cpu is running, 1 cpu is going to die.
15350c7960aSSoren Brinkmann  *
15450c7960aSSoren Brinkmann  * Return: true if cpu is running, false if cpu is going to die
15550c7960aSSoren Brinkmann  */
zynq_slcr_cpu_state_read(int cpu)15650c7960aSSoren Brinkmann bool zynq_slcr_cpu_state_read(int cpu)
15750c7960aSSoren Brinkmann {
15850c7960aSSoren Brinkmann 	u32 state;
15950c7960aSSoren Brinkmann 
16050c7960aSSoren Brinkmann 	state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
16150c7960aSSoren Brinkmann 	state &= 1 << (31 - cpu);
16250c7960aSSoren Brinkmann 
16350c7960aSSoren Brinkmann 	return !state;
16450c7960aSSoren Brinkmann }
16550c7960aSSoren Brinkmann 
16650c7960aSSoren Brinkmann /**
167*554add03SRandy Dunlap  * zynq_slcr_cpu_state_write - Write cpu state
16850c7960aSSoren Brinkmann  * @cpu:	cpu number
16950c7960aSSoren Brinkmann  * @die:	cpu state - true if cpu is going to die
17050c7960aSSoren Brinkmann  *
17150c7960aSSoren Brinkmann  * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1)
17250c7960aSSoren Brinkmann  * 0 means cpu is running, 1 cpu is going to die.
17350c7960aSSoren Brinkmann  */
zynq_slcr_cpu_state_write(int cpu,bool die)17450c7960aSSoren Brinkmann void zynq_slcr_cpu_state_write(int cpu, bool die)
17550c7960aSSoren Brinkmann {
17650c7960aSSoren Brinkmann 	u32 state, mask;
17750c7960aSSoren Brinkmann 
17850c7960aSSoren Brinkmann 	state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
17950c7960aSSoren Brinkmann 	mask = 1 << (31 - cpu);
18050c7960aSSoren Brinkmann 	if (die)
18150c7960aSSoren Brinkmann 		state |= mask;
18250c7960aSSoren Brinkmann 	else
18350c7960aSSoren Brinkmann 		state &= ~mask;
18450c7960aSSoren Brinkmann 	writel(state, zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
18550c7960aSSoren Brinkmann }
18650c7960aSSoren Brinkmann 
18750c7960aSSoren Brinkmann /**
188016f4dcaSMichal Simek  * zynq_early_slcr_init - Early slcr init function
189016f4dcaSMichal Simek  *
190016f4dcaSMichal Simek  * Return:	0 on success, negative errno otherwise.
191016f4dcaSMichal Simek  *
192016f4dcaSMichal Simek  * Called very early during boot from platform code to unlock SLCR.
193016f4dcaSMichal Simek  */
zynq_early_slcr_init(void)194016f4dcaSMichal Simek int __init zynq_early_slcr_init(void)
195016f4dcaSMichal Simek {
19664b889b3SMichal Simek 	struct device_node *np;
19764b889b3SMichal Simek 
19864b889b3SMichal Simek 	np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
19964b889b3SMichal Simek 	if (!np) {
20064b889b3SMichal Simek 		pr_err("%s: no slcr node found\n", __func__);
20164b889b3SMichal Simek 		BUG();
20264b889b3SMichal Simek 	}
20364b889b3SMichal Simek 
20464b889b3SMichal Simek 	zynq_slcr_base = of_iomap(np, 0);
20564b889b3SMichal Simek 	if (!zynq_slcr_base) {
20664b889b3SMichal Simek 		pr_err("%s: Unable to map I/O memory\n", __func__);
20764b889b3SMichal Simek 		BUG();
20864b889b3SMichal Simek 	}
20964b889b3SMichal Simek 
2105e218280SSteffen Trumtrar 	np->data = (__force void *)zynq_slcr_base;
2115e218280SSteffen Trumtrar 
2123329659dSMichal Simek 	zynq_slcr_regmap = syscon_regmap_lookup_by_compatible("xlnx,zynq-slcr");
2133329659dSMichal Simek 	if (IS_ERR(zynq_slcr_regmap)) {
2143329659dSMichal Simek 		pr_err("%s: failed to find zynq-slcr\n", __func__);
2159eedb910SQiheng Lin 		of_node_put(np);
2163329659dSMichal Simek 		return -ENODEV;
2173329659dSMichal Simek 	}
2183329659dSMichal Simek 
21964b889b3SMichal Simek 	/* unlock the SLCR so that registers can be changed */
22056880073SMichal Simek 	zynq_slcr_unlock();
22164b889b3SMichal Simek 
2226ded93a1SJosh Cartwright 	/* See AR#54190 design advisory */
2236ded93a1SJosh Cartwright 	regmap_update_bits(zynq_slcr_regmap, SLCR_L2C_RAM, 0x70707, 0x20202);
2246ded93a1SJosh Cartwright 
22564e68617SJosh Cartwright 	register_restart_handler(&zynq_slcr_restart_nb);
22664e68617SJosh Cartwright 
22720487a8dSRob Herring 	pr_info("%pOFn mapped to %p\n", np, zynq_slcr_base);
22864b889b3SMichal Simek 
22964b889b3SMichal Simek 	of_node_put(np);
23064b889b3SMichal Simek 
23164b889b3SMichal Simek 	return 0;
23264b889b3SMichal Simek }
233