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(®, 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(®, 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