xref: /linux/arch/arm/mach-zynq/slcr.c (revision 20487a8ddf207dde042a4463e3f82ab17c17cf13)
164b889b3SMichal Simek /*
264b889b3SMichal Simek  * Xilinx SLCR driver
364b889b3SMichal Simek  *
464b889b3SMichal Simek  * Copyright (c) 2011-2013 Xilinx Inc.
564b889b3SMichal Simek  *
664b889b3SMichal Simek  * This program is free software; you can redistribute it and/or
764b889b3SMichal Simek  * modify it under the terms of the GNU General Public License
864b889b3SMichal Simek  * as published by the Free Software Foundation; either version
964b889b3SMichal Simek  * 2 of the License, or (at your option) any later version.
1064b889b3SMichal Simek  *
1164b889b3SMichal Simek  * You should have received a copy of the GNU General Public
1264b889b3SMichal Simek  * License along with this program; if not, write to the Free
1364b889b3SMichal Simek  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
1464b889b3SMichal Simek  * 02139, USA.
1564b889b3SMichal Simek  */
1664b889b3SMichal Simek 
1764b889b3SMichal Simek #include <linux/io.h>
1864e68617SJosh Cartwright #include <linux/reboot.h>
19016f4dcaSMichal Simek #include <linux/mfd/syscon.h>
2064b889b3SMichal Simek #include <linux/of_address.h>
21016f4dcaSMichal Simek #include <linux/regmap.h>
2264b889b3SMichal Simek #include <linux/clk/zynq.h>
2364b889b3SMichal Simek #include "common.h"
2464b889b3SMichal Simek 
25b5f177ffSSoren Brinkmann /* register offsets */
26b5f177ffSSoren Brinkmann #define SLCR_UNLOCK_OFFSET		0x8   /* SCLR unlock register */
2796790f0aSMichal Simek #define SLCR_PS_RST_CTRL_OFFSET		0x200 /* PS Software Reset Control */
28b5f177ffSSoren Brinkmann #define SLCR_A9_CPU_RST_CTRL_OFFSET	0x244 /* CPU Software Reset Control */
29b5f177ffSSoren Brinkmann #define SLCR_REBOOT_STATUS_OFFSET	0x258 /* PS Reboot Status */
3000f7dc63SMichal Simek #define SLCR_PSS_IDCODE			0x530 /* PS IDCODE */
316ded93a1SJosh Cartwright #define SLCR_L2C_RAM			0xA1C /* L2C_RAM in AR#54190 */
32aa7eb2bbSMichal Simek 
33b5f177ffSSoren Brinkmann #define SLCR_UNLOCK_MAGIC		0xDF0D
34aa7eb2bbSMichal Simek #define SLCR_A9_CPU_CLKSTOP		0x10
35aa7eb2bbSMichal Simek #define SLCR_A9_CPU_RST			0x1
3600f7dc63SMichal Simek #define SLCR_PSS_IDCODE_DEVICE_SHIFT	12
3700f7dc63SMichal Simek #define SLCR_PSS_IDCODE_DEVICE_MASK	0x1F
38aa7eb2bbSMichal Simek 
397b274efeSSteffen Trumtrar static void __iomem *zynq_slcr_base;
40016f4dcaSMichal Simek static struct regmap *zynq_slcr_regmap;
4164b889b3SMichal Simek 
4264b889b3SMichal Simek /**
43871c6971SMichal Simek  * zynq_slcr_write - Write to a register in SLCR block
44871c6971SMichal Simek  *
45871c6971SMichal Simek  * @val:	Value to write to the register
46871c6971SMichal Simek  * @offset:	Register offset in SLCR block
47871c6971SMichal Simek  *
48871c6971SMichal Simek  * Return:	a negative value on error, 0 on success
49871c6971SMichal Simek  */
50871c6971SMichal Simek static int zynq_slcr_write(u32 val, u32 offset)
51871c6971SMichal Simek {
52871c6971SMichal Simek 	return regmap_write(zynq_slcr_regmap, offset, val);
53871c6971SMichal Simek }
54871c6971SMichal Simek 
55871c6971SMichal Simek /**
56871c6971SMichal Simek  * zynq_slcr_read - Read a register in SLCR block
57871c6971SMichal Simek  *
58871c6971SMichal Simek  * @val:	Pointer to value to be read from SLCR
59871c6971SMichal Simek  * @offset:	Register offset in SLCR block
60871c6971SMichal Simek  *
61871c6971SMichal Simek  * Return:	a negative value on error, 0 on success
62871c6971SMichal Simek  */
63871c6971SMichal Simek static int zynq_slcr_read(u32 *val, u32 offset)
64871c6971SMichal Simek {
65871c6971SMichal Simek 	return regmap_read(zynq_slcr_regmap, offset, val);
66871c6971SMichal Simek }
67871c6971SMichal Simek 
68871c6971SMichal Simek /**
6956880073SMichal Simek  * zynq_slcr_unlock - Unlock SLCR registers
7056880073SMichal Simek  *
7156880073SMichal Simek  * Return:	a negative value on error, 0 on success
7256880073SMichal Simek  */
7356880073SMichal Simek static inline int zynq_slcr_unlock(void)
7456880073SMichal Simek {
7556880073SMichal Simek 	zynq_slcr_write(SLCR_UNLOCK_MAGIC, SLCR_UNLOCK_OFFSET);
7656880073SMichal Simek 
7756880073SMichal Simek 	return 0;
7856880073SMichal Simek }
7956880073SMichal Simek 
8056880073SMichal Simek /**
8100f7dc63SMichal Simek  * zynq_slcr_get_device_id - Read device code id
8200f7dc63SMichal Simek  *
8300f7dc63SMichal Simek  * Return:	Device code id
8400f7dc63SMichal Simek  */
8500f7dc63SMichal Simek u32 zynq_slcr_get_device_id(void)
8600f7dc63SMichal Simek {
8700f7dc63SMichal Simek 	u32 val;
8800f7dc63SMichal Simek 
8900f7dc63SMichal Simek 	zynq_slcr_read(&val, SLCR_PSS_IDCODE);
9000f7dc63SMichal Simek 	val >>= SLCR_PSS_IDCODE_DEVICE_SHIFT;
9100f7dc63SMichal Simek 	val &= SLCR_PSS_IDCODE_DEVICE_MASK;
9200f7dc63SMichal Simek 
9300f7dc63SMichal Simek 	return val;
9400f7dc63SMichal Simek }
9500f7dc63SMichal Simek 
9600f7dc63SMichal Simek /**
9764e68617SJosh Cartwright  * zynq_slcr_system_restart - Restart the entire system.
9864e68617SJosh Cartwright  *
9964e68617SJosh Cartwright  * @nb:		Pointer to restart notifier block (unused)
10064e68617SJosh Cartwright  * @action:	Reboot mode (unused)
10164e68617SJosh Cartwright  * @data:	Restart handler private data (unused)
10264e68617SJosh Cartwright  *
10364e68617SJosh Cartwright  * Return:	0 always
10496790f0aSMichal Simek  */
10564e68617SJosh Cartwright static
10664e68617SJosh Cartwright int zynq_slcr_system_restart(struct notifier_block *nb,
10764e68617SJosh Cartwright 			     unsigned long action, void *data)
10896790f0aSMichal Simek {
10996790f0aSMichal Simek 	u32 reboot;
11096790f0aSMichal Simek 
11196790f0aSMichal Simek 	/*
11296790f0aSMichal Simek 	 * Clear 0x0F000000 bits of reboot status register to workaround
11396790f0aSMichal Simek 	 * the FSBL not loading the bitstream after soft-reboot
11496790f0aSMichal Simek 	 * This is a temporary solution until we know more.
11596790f0aSMichal Simek 	 */
116871c6971SMichal Simek 	zynq_slcr_read(&reboot, SLCR_REBOOT_STATUS_OFFSET);
117871c6971SMichal Simek 	zynq_slcr_write(reboot & 0xF0FFFFFF, SLCR_REBOOT_STATUS_OFFSET);
118871c6971SMichal Simek 	zynq_slcr_write(1, SLCR_PS_RST_CTRL_OFFSET);
11964e68617SJosh Cartwright 	return 0;
12096790f0aSMichal Simek }
12196790f0aSMichal Simek 
12264e68617SJosh Cartwright static struct notifier_block zynq_slcr_restart_nb = {
12364e68617SJosh Cartwright 	.notifier_call	= zynq_slcr_system_restart,
12464e68617SJosh Cartwright 	.priority	= 192,
12564e68617SJosh Cartwright };
12664e68617SJosh Cartwright 
12796790f0aSMichal Simek /**
128aa7eb2bbSMichal Simek  * zynq_slcr_cpu_start - Start cpu
129aa7eb2bbSMichal Simek  * @cpu:	cpu number
130aa7eb2bbSMichal Simek  */
131aa7eb2bbSMichal Simek void zynq_slcr_cpu_start(int cpu)
132aa7eb2bbSMichal Simek {
133871c6971SMichal Simek 	u32 reg;
134871c6971SMichal Simek 
135871c6971SMichal Simek 	zynq_slcr_read(&reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
1363db9e860SSoren Brinkmann 	reg &= ~(SLCR_A9_CPU_RST << cpu);
137871c6971SMichal Simek 	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
1383db9e860SSoren Brinkmann 	reg &= ~(SLCR_A9_CPU_CLKSTOP << cpu);
139871c6971SMichal Simek 	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
14050c7960aSSoren Brinkmann 
14150c7960aSSoren Brinkmann 	zynq_slcr_cpu_state_write(cpu, false);
142aa7eb2bbSMichal Simek }
143aa7eb2bbSMichal Simek 
144aa7eb2bbSMichal Simek /**
145aa7eb2bbSMichal Simek  * zynq_slcr_cpu_stop - Stop cpu
146aa7eb2bbSMichal Simek  * @cpu:	cpu number
147aa7eb2bbSMichal Simek  */
148aa7eb2bbSMichal Simek void zynq_slcr_cpu_stop(int cpu)
149aa7eb2bbSMichal Simek {
150871c6971SMichal Simek 	u32 reg;
151871c6971SMichal Simek 
152871c6971SMichal Simek 	zynq_slcr_read(&reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
1533db9e860SSoren Brinkmann 	reg |= (SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu;
154871c6971SMichal Simek 	zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET);
155aa7eb2bbSMichal Simek }
156aa7eb2bbSMichal Simek 
157aa7eb2bbSMichal Simek /**
15850c7960aSSoren Brinkmann  * zynq_slcr_cpu_state - Read/write cpu state
15950c7960aSSoren Brinkmann  * @cpu:	cpu number
160016f4dcaSMichal Simek  *
16150c7960aSSoren Brinkmann  * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1)
16250c7960aSSoren Brinkmann  * 0 means cpu is running, 1 cpu is going to die.
16350c7960aSSoren Brinkmann  *
16450c7960aSSoren Brinkmann  * Return: true if cpu is running, false if cpu is going to die
16550c7960aSSoren Brinkmann  */
16650c7960aSSoren Brinkmann bool zynq_slcr_cpu_state_read(int cpu)
16750c7960aSSoren Brinkmann {
16850c7960aSSoren Brinkmann 	u32 state;
16950c7960aSSoren Brinkmann 
17050c7960aSSoren Brinkmann 	state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
17150c7960aSSoren Brinkmann 	state &= 1 << (31 - cpu);
17250c7960aSSoren Brinkmann 
17350c7960aSSoren Brinkmann 	return !state;
17450c7960aSSoren Brinkmann }
17550c7960aSSoren Brinkmann 
17650c7960aSSoren Brinkmann /**
17750c7960aSSoren Brinkmann  * zynq_slcr_cpu_state - Read/write cpu state
17850c7960aSSoren Brinkmann  * @cpu:	cpu number
17950c7960aSSoren Brinkmann  * @die:	cpu state - true if cpu is going to die
18050c7960aSSoren Brinkmann  *
18150c7960aSSoren Brinkmann  * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1)
18250c7960aSSoren Brinkmann  * 0 means cpu is running, 1 cpu is going to die.
18350c7960aSSoren Brinkmann  */
18450c7960aSSoren Brinkmann void zynq_slcr_cpu_state_write(int cpu, bool die)
18550c7960aSSoren Brinkmann {
18650c7960aSSoren Brinkmann 	u32 state, mask;
18750c7960aSSoren Brinkmann 
18850c7960aSSoren Brinkmann 	state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
18950c7960aSSoren Brinkmann 	mask = 1 << (31 - cpu);
19050c7960aSSoren Brinkmann 	if (die)
19150c7960aSSoren Brinkmann 		state |= mask;
19250c7960aSSoren Brinkmann 	else
19350c7960aSSoren Brinkmann 		state &= ~mask;
19450c7960aSSoren Brinkmann 	writel(state, zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET);
19550c7960aSSoren Brinkmann }
19650c7960aSSoren Brinkmann 
19750c7960aSSoren Brinkmann /**
198016f4dcaSMichal Simek  * zynq_early_slcr_init - Early slcr init function
199016f4dcaSMichal Simek  *
200016f4dcaSMichal Simek  * Return:	0 on success, negative errno otherwise.
201016f4dcaSMichal Simek  *
202016f4dcaSMichal Simek  * Called very early during boot from platform code to unlock SLCR.
203016f4dcaSMichal Simek  */
204016f4dcaSMichal Simek int __init zynq_early_slcr_init(void)
205016f4dcaSMichal Simek {
20664b889b3SMichal Simek 	struct device_node *np;
20764b889b3SMichal Simek 
20864b889b3SMichal Simek 	np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr");
20964b889b3SMichal Simek 	if (!np) {
21064b889b3SMichal Simek 		pr_err("%s: no slcr node found\n", __func__);
21164b889b3SMichal Simek 		BUG();
21264b889b3SMichal Simek 	}
21364b889b3SMichal Simek 
21464b889b3SMichal Simek 	zynq_slcr_base = of_iomap(np, 0);
21564b889b3SMichal Simek 	if (!zynq_slcr_base) {
21664b889b3SMichal Simek 		pr_err("%s: Unable to map I/O memory\n", __func__);
21764b889b3SMichal Simek 		BUG();
21864b889b3SMichal Simek 	}
21964b889b3SMichal Simek 
2205e218280SSteffen Trumtrar 	np->data = (__force void *)zynq_slcr_base;
2215e218280SSteffen Trumtrar 
2223329659dSMichal Simek 	zynq_slcr_regmap = syscon_regmap_lookup_by_compatible("xlnx,zynq-slcr");
2233329659dSMichal Simek 	if (IS_ERR(zynq_slcr_regmap)) {
2243329659dSMichal Simek 		pr_err("%s: failed to find zynq-slcr\n", __func__);
2253329659dSMichal Simek 		return -ENODEV;
2263329659dSMichal Simek 	}
2273329659dSMichal Simek 
22864b889b3SMichal Simek 	/* unlock the SLCR so that registers can be changed */
22956880073SMichal Simek 	zynq_slcr_unlock();
23064b889b3SMichal Simek 
2316ded93a1SJosh Cartwright 	/* See AR#54190 design advisory */
2326ded93a1SJosh Cartwright 	regmap_update_bits(zynq_slcr_regmap, SLCR_L2C_RAM, 0x70707, 0x20202);
2336ded93a1SJosh Cartwright 
23464e68617SJosh Cartwright 	register_restart_handler(&zynq_slcr_restart_nb);
23564e68617SJosh Cartwright 
236*20487a8dSRob Herring 	pr_info("%pOFn mapped to %p\n", np, zynq_slcr_base);
23764b889b3SMichal Simek 
23864b889b3SMichal Simek 	of_node_put(np);
23964b889b3SMichal Simek 
24064b889b3SMichal Simek 	return 0;
24164b889b3SMichal Simek }
242