xref: /linux/arch/arm/mach-zynq/slcr.c (revision 554add032d9d10cd4a31ceb2ba6cd50a102ba805)
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 <linux/clk/zynq.h>
1464b889b3SMichal Simek #include "common.h"
1564b889b3SMichal Simek 
16b5f177ffSSoren Brinkmann /* register offsets */
17b5f177ffSSoren Brinkmann #define SLCR_UNLOCK_OFFSET		0x8   /* SCLR unlock register */
1896790f0aSMichal Simek #define SLCR_PS_RST_CTRL_OFFSET		0x200 /* PS Software Reset Control */
19b5f177ffSSoren Brinkmann #define SLCR_A9_CPU_RST_CTRL_OFFSET	0x244 /* CPU Software Reset Control */
20b5f177ffSSoren Brinkmann #define SLCR_REBOOT_STATUS_OFFSET	0x258 /* PS Reboot Status */
2100f7dc63SMichal Simek #define SLCR_PSS_IDCODE			0x530 /* PS IDCODE */
226ded93a1SJosh Cartwright #define SLCR_L2C_RAM			0xA1C /* L2C_RAM in AR#54190 */
23aa7eb2bbSMichal Simek 
24b5f177ffSSoren Brinkmann #define SLCR_UNLOCK_MAGIC		0xDF0D
25aa7eb2bbSMichal Simek #define SLCR_A9_CPU_CLKSTOP		0x10
26aa7eb2bbSMichal Simek #define SLCR_A9_CPU_RST			0x1
2700f7dc63SMichal Simek #define SLCR_PSS_IDCODE_DEVICE_SHIFT	12
2800f7dc63SMichal Simek #define SLCR_PSS_IDCODE_DEVICE_MASK	0x1F
29aa7eb2bbSMichal Simek 
307b274efeSSteffen Trumtrar static void __iomem *zynq_slcr_base;
31016f4dcaSMichal Simek static struct regmap *zynq_slcr_regmap;
3264b889b3SMichal Simek 
3364b889b3SMichal Simek /**
34871c6971SMichal Simek  * zynq_slcr_write - Write to a register in SLCR block
35871c6971SMichal Simek  *
36871c6971SMichal Simek  * @val:	Value to write to the register
37871c6971SMichal Simek  * @offset:	Register offset in SLCR block
38871c6971SMichal Simek  *
39871c6971SMichal Simek  * Return:	a negative value on error, 0 on success
40871c6971SMichal Simek  */
41871c6971SMichal Simek static int zynq_slcr_write(u32 val, u32 offset)
42871c6971SMichal Simek {
43871c6971SMichal Simek 	return regmap_write(zynq_slcr_regmap, offset, val);
44871c6971SMichal Simek }
45871c6971SMichal Simek 
46871c6971SMichal Simek /**
47871c6971SMichal Simek  * zynq_slcr_read - Read a register in SLCR block
48871c6971SMichal Simek  *
49871c6971SMichal Simek  * @val:	Pointer to value to be read from SLCR
50871c6971SMichal Simek  * @offset:	Register offset in SLCR block
51871c6971SMichal Simek  *
52871c6971SMichal Simek  * Return:	a negative value on error, 0 on success
53871c6971SMichal Simek  */
54871c6971SMichal Simek static int zynq_slcr_read(u32 *val, u32 offset)
55871c6971SMichal Simek {
56871c6971SMichal Simek 	return regmap_read(zynq_slcr_regmap, offset, val);
57871c6971SMichal Simek }
58871c6971SMichal Simek 
59871c6971SMichal Simek /**
6056880073SMichal Simek  * zynq_slcr_unlock - Unlock SLCR registers
6156880073SMichal Simek  *
6256880073SMichal Simek  * Return:	a negative value on error, 0 on success
6356880073SMichal Simek  */
6456880073SMichal Simek static inline int zynq_slcr_unlock(void)
6556880073SMichal Simek {
6656880073SMichal Simek 	zynq_slcr_write(SLCR_UNLOCK_MAGIC, SLCR_UNLOCK_OFFSET);
6756880073SMichal Simek 
6856880073SMichal Simek 	return 0;
6956880073SMichal Simek }
7056880073SMichal Simek 
7156880073SMichal Simek /**
7200f7dc63SMichal Simek  * zynq_slcr_get_device_id - Read device code id
7300f7dc63SMichal Simek  *
7400f7dc63SMichal Simek  * Return:	Device code id
7500f7dc63SMichal Simek  */
7600f7dc63SMichal Simek u32 zynq_slcr_get_device_id(void)
7700f7dc63SMichal Simek {
7800f7dc63SMichal Simek 	u32 val;
7900f7dc63SMichal Simek 
8000f7dc63SMichal Simek 	zynq_slcr_read(&val, SLCR_PSS_IDCODE);
8100f7dc63SMichal Simek 	val >>= SLCR_PSS_IDCODE_DEVICE_SHIFT;
8200f7dc63SMichal Simek 	val &= SLCR_PSS_IDCODE_DEVICE_MASK;
8300f7dc63SMichal Simek 
8400f7dc63SMichal Simek 	return val;
8500f7dc63SMichal Simek }
8600f7dc63SMichal Simek 
8700f7dc63SMichal Simek /**
8864e68617SJosh Cartwright  * zynq_slcr_system_restart - Restart the entire system.
8964e68617SJosh Cartwright  *
9064e68617SJosh Cartwright  * @nb:		Pointer to restart notifier block (unused)
9164e68617SJosh Cartwright  * @action:	Reboot mode (unused)
9264e68617SJosh Cartwright  * @data:	Restart handler private data (unused)
9364e68617SJosh Cartwright  *
9464e68617SJosh Cartwright  * Return:	0 always
9596790f0aSMichal Simek  */
9664e68617SJosh Cartwright static
9764e68617SJosh Cartwright int zynq_slcr_system_restart(struct notifier_block *nb,
9864e68617SJosh Cartwright 			     unsigned long action, void *data)
9996790f0aSMichal Simek {
10096790f0aSMichal Simek 	u32 reboot;
10196790f0aSMichal Simek 
10296790f0aSMichal Simek 	/*
10396790f0aSMichal Simek 	 * Clear 0x0F000000 bits of reboot status register to workaround
10496790f0aSMichal Simek 	 * the FSBL not loading the bitstream after soft-reboot
10596790f0aSMichal Simek 	 * This is a temporary solution until we know more.
10696790f0aSMichal Simek 	 */
107871c6971SMichal Simek 	zynq_slcr_read(&reboot, SLCR_REBOOT_STATUS_OFFSET);
108871c6971SMichal Simek 	zynq_slcr_write(reboot & 0xF0FFFFFF, SLCR_REBOOT_STATUS_OFFSET);
109871c6971SMichal Simek 	zynq_slcr_write(1, SLCR_PS_RST_CTRL_OFFSET);
11064e68617SJosh Cartwright 	return 0;
11196790f0aSMichal Simek }
11296790f0aSMichal Simek 
11364e68617SJosh Cartwright static struct notifier_block zynq_slcr_restart_nb = {
11464e68617SJosh Cartwright 	.notifier_call	= zynq_slcr_system_restart,
11564e68617SJosh Cartwright 	.priority	= 192,
11664e68617SJosh Cartwright };
11764e68617SJosh Cartwright 
11896790f0aSMichal Simek /**
119aa7eb2bbSMichal Simek  * zynq_slcr_cpu_start - Start cpu
120aa7eb2bbSMichal Simek  * @cpu:	cpu number
121aa7eb2bbSMichal Simek  */
122aa7eb2bbSMichal Simek void zynq_slcr_cpu_start(int cpu)
123aa7eb2bbSMichal Simek {
124871c6971SMichal Simek 	u32 reg;
125871c6971SMichal Simek 
126871c6971SMichal Simek 	zynq_slcr_read(&reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
1273db9e860SSoren Brinkmann 	reg &= ~(SLCR_A9_CPU_RST << cpu);
128871c6971SMichal Simek 	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
1293db9e860SSoren Brinkmann 	reg &= ~(SLCR_A9_CPU_CLKSTOP << cpu);
130871c6971SMichal Simek 	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
13150c7960aSSoren Brinkmann 
13250c7960aSSoren Brinkmann 	zynq_slcr_cpu_state_write(cpu, false);
133aa7eb2bbSMichal Simek }
134aa7eb2bbSMichal Simek 
135aa7eb2bbSMichal Simek /**
136aa7eb2bbSMichal Simek  * zynq_slcr_cpu_stop - Stop cpu
137aa7eb2bbSMichal Simek  * @cpu:	cpu number
138aa7eb2bbSMichal Simek  */
139aa7eb2bbSMichal Simek void zynq_slcr_cpu_stop(int cpu)
140aa7eb2bbSMichal Simek {
141871c6971SMichal Simek 	u32 reg;
142871c6971SMichal Simek 
143871c6971SMichal Simek 	zynq_slcr_read(&reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
1443db9e860SSoren Brinkmann 	reg |= (SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu;
145871c6971SMichal Simek 	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
146aa7eb2bbSMichal Simek }
147aa7eb2bbSMichal Simek 
148aa7eb2bbSMichal Simek /**
149*554add03SRandy Dunlap  * zynq_slcr_cpu_state_read - Read cpu state
15050c7960aSSoren Brinkmann  * @cpu:	cpu number
151016f4dcaSMichal Simek  *
15250c7960aSSoren Brinkmann  * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1)
15350c7960aSSoren Brinkmann  * 0 means cpu is running, 1 cpu is going to die.
15450c7960aSSoren Brinkmann  *
15550c7960aSSoren Brinkmann  * Return: true if cpu is running, false if cpu is going to die
15650c7960aSSoren Brinkmann  */
15750c7960aSSoren Brinkmann bool zynq_slcr_cpu_state_read(int cpu)
15850c7960aSSoren Brinkmann {
15950c7960aSSoren Brinkmann 	u32 state;
16050c7960aSSoren Brinkmann 
16150c7960aSSoren Brinkmann 	state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
16250c7960aSSoren Brinkmann 	state &= 1 << (31 - cpu);
16350c7960aSSoren Brinkmann 
16450c7960aSSoren Brinkmann 	return !state;
16550c7960aSSoren Brinkmann }
16650c7960aSSoren Brinkmann 
16750c7960aSSoren Brinkmann /**
168*554add03SRandy Dunlap  * zynq_slcr_cpu_state_write - Write cpu state
16950c7960aSSoren Brinkmann  * @cpu:	cpu number
17050c7960aSSoren Brinkmann  * @die:	cpu state - true if cpu is going to die
17150c7960aSSoren Brinkmann  *
17250c7960aSSoren Brinkmann  * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1)
17350c7960aSSoren Brinkmann  * 0 means cpu is running, 1 cpu is going to die.
17450c7960aSSoren Brinkmann  */
17550c7960aSSoren Brinkmann void zynq_slcr_cpu_state_write(int cpu, bool die)
17650c7960aSSoren Brinkmann {
17750c7960aSSoren Brinkmann 	u32 state, mask;
17850c7960aSSoren Brinkmann 
17950c7960aSSoren Brinkmann 	state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
18050c7960aSSoren Brinkmann 	mask = 1 << (31 - cpu);
18150c7960aSSoren Brinkmann 	if (die)
18250c7960aSSoren Brinkmann 		state |= mask;
18350c7960aSSoren Brinkmann 	else
18450c7960aSSoren Brinkmann 		state &= ~mask;
18550c7960aSSoren Brinkmann 	writel(state, zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
18650c7960aSSoren Brinkmann }
18750c7960aSSoren Brinkmann 
18850c7960aSSoren Brinkmann /**
189016f4dcaSMichal Simek  * zynq_early_slcr_init - Early slcr init function
190016f4dcaSMichal Simek  *
191016f4dcaSMichal Simek  * Return:	0 on success, negative errno otherwise.
192016f4dcaSMichal Simek  *
193016f4dcaSMichal Simek  * Called very early during boot from platform code to unlock SLCR.
194016f4dcaSMichal Simek  */
195016f4dcaSMichal Simek int __init zynq_early_slcr_init(void)
196016f4dcaSMichal Simek {
19764b889b3SMichal Simek 	struct device_node *np;
19864b889b3SMichal Simek 
19964b889b3SMichal Simek 	np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
20064b889b3SMichal Simek 	if (!np) {
20164b889b3SMichal Simek 		pr_err("%s: no slcr node found\n", __func__);
20264b889b3SMichal Simek 		BUG();
20364b889b3SMichal Simek 	}
20464b889b3SMichal Simek 
20564b889b3SMichal Simek 	zynq_slcr_base = of_iomap(np, 0);
20664b889b3SMichal Simek 	if (!zynq_slcr_base) {
20764b889b3SMichal Simek 		pr_err("%s: Unable to map I/O memory\n", __func__);
20864b889b3SMichal Simek 		BUG();
20964b889b3SMichal Simek 	}
21064b889b3SMichal Simek 
2115e218280SSteffen Trumtrar 	np->data = (__force void *)zynq_slcr_base;
2125e218280SSteffen Trumtrar 
2133329659dSMichal Simek 	zynq_slcr_regmap = syscon_regmap_lookup_by_compatible("xlnx,zynq-slcr");
2143329659dSMichal Simek 	if (IS_ERR(zynq_slcr_regmap)) {
2153329659dSMichal Simek 		pr_err("%s: failed to find zynq-slcr\n", __func__);
2169eedb910SQiheng Lin 		of_node_put(np);
2173329659dSMichal Simek 		return -ENODEV;
2183329659dSMichal Simek 	}
2193329659dSMichal Simek 
22064b889b3SMichal Simek 	/* unlock the SLCR so that registers can be changed */
22156880073SMichal Simek 	zynq_slcr_unlock();
22264b889b3SMichal Simek 
2236ded93a1SJosh Cartwright 	/* See AR#54190 design advisory */
2246ded93a1SJosh Cartwright 	regmap_update_bits(zynq_slcr_regmap, SLCR_L2C_RAM, 0x70707, 0x20202);
2256ded93a1SJosh Cartwright 
22664e68617SJosh Cartwright 	register_restart_handler(&zynq_slcr_restart_nb);
22764e68617SJosh Cartwright 
22820487a8dSRob Herring 	pr_info("%pOFn mapped to %p\n", np, zynq_slcr_base);
22964b889b3SMichal Simek 
23064b889b3SMichal Simek 	of_node_put(np);
23164b889b3SMichal Simek 
23264b889b3SMichal Simek 	return 0;
23364b889b3SMichal Simek }
234