1 /* 2 * Xilinx SLCR driver 3 * 4 * Copyright (c) 2011-2013 Xilinx Inc. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 * 11 * You should have received a copy of the GNU General Public 12 * License along with this program; if not, write to the Free 13 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 14 * 02139, USA. 15 */ 16 17 #include <linux/io.h> 18 #include <linux/mfd/syscon.h> 19 #include <linux/of_address.h> 20 #include <linux/regmap.h> 21 #include <linux/clk/zynq.h> 22 #include "common.h" 23 24 /* register offsets */ 25 #define SLCR_UNLOCK_OFFSET 0x8 /* SCLR unlock register */ 26 #define SLCR_PS_RST_CTRL_OFFSET 0x200 /* PS Software Reset Control */ 27 #define SLCR_A9_CPU_RST_CTRL_OFFSET 0x244 /* CPU Software Reset Control */ 28 #define SLCR_REBOOT_STATUS_OFFSET 0x258 /* PS Reboot Status */ 29 #define SLCR_PSS_IDCODE 0x530 /* PS IDCODE */ 30 31 #define SLCR_UNLOCK_MAGIC 0xDF0D 32 #define SLCR_A9_CPU_CLKSTOP 0x10 33 #define SLCR_A9_CPU_RST 0x1 34 #define SLCR_PSS_IDCODE_DEVICE_SHIFT 12 35 #define SLCR_PSS_IDCODE_DEVICE_MASK 0x1F 36 37 static void __iomem *zynq_slcr_base; 38 static struct regmap *zynq_slcr_regmap; 39 40 /** 41 * zynq_slcr_write - Write to a register in SLCR block 42 * 43 * @val: Value to write to the register 44 * @offset: Register offset in SLCR block 45 * 46 * Return: a negative value on error, 0 on success 47 */ 48 static int zynq_slcr_write(u32 val, u32 offset) 49 { 50 if (!zynq_slcr_regmap) { 51 writel(val, zynq_slcr_base + offset); 52 return 0; 53 } 54 55 return regmap_write(zynq_slcr_regmap, offset, val); 56 } 57 58 /** 59 * zynq_slcr_read - Read a register in SLCR block 60 * 61 * @val: Pointer to value to be read from SLCR 62 * @offset: Register offset in SLCR block 63 * 64 * Return: a negative value on error, 0 on success 65 */ 66 static int zynq_slcr_read(u32 *val, u32 offset) 67 { 68 if (zynq_slcr_regmap) 69 return regmap_read(zynq_slcr_regmap, offset, val); 70 71 *val = readl(zynq_slcr_base + offset); 72 73 return 0; 74 } 75 76 /** 77 * zynq_slcr_unlock - Unlock SLCR registers 78 * 79 * Return: a negative value on error, 0 on success 80 */ 81 static inline int zynq_slcr_unlock(void) 82 { 83 zynq_slcr_write(SLCR_UNLOCK_MAGIC, SLCR_UNLOCK_OFFSET); 84 85 return 0; 86 } 87 88 /** 89 * zynq_slcr_get_device_id - Read device code id 90 * 91 * Return: Device code id 92 */ 93 u32 zynq_slcr_get_device_id(void) 94 { 95 u32 val; 96 97 zynq_slcr_read(&val, SLCR_PSS_IDCODE); 98 val >>= SLCR_PSS_IDCODE_DEVICE_SHIFT; 99 val &= SLCR_PSS_IDCODE_DEVICE_MASK; 100 101 return val; 102 } 103 104 /** 105 * zynq_slcr_system_reset - Reset the entire system. 106 */ 107 void zynq_slcr_system_reset(void) 108 { 109 u32 reboot; 110 111 /* 112 * Unlock the SLCR then reset the system. 113 * Note that this seems to require raw i/o 114 * functions or there's a lockup? 115 */ 116 zynq_slcr_unlock(); 117 118 /* 119 * Clear 0x0F000000 bits of reboot status register to workaround 120 * the FSBL not loading the bitstream after soft-reboot 121 * This is a temporary solution until we know more. 122 */ 123 zynq_slcr_read(&reboot, SLCR_REBOOT_STATUS_OFFSET); 124 zynq_slcr_write(reboot & 0xF0FFFFFF, SLCR_REBOOT_STATUS_OFFSET); 125 zynq_slcr_write(1, SLCR_PS_RST_CTRL_OFFSET); 126 } 127 128 /** 129 * zynq_slcr_cpu_start - Start cpu 130 * @cpu: cpu number 131 */ 132 void zynq_slcr_cpu_start(int cpu) 133 { 134 u32 reg; 135 136 zynq_slcr_read(®, SLCR_A9_CPU_RST_CTRL_OFFSET); 137 reg &= ~(SLCR_A9_CPU_RST << cpu); 138 zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET); 139 reg &= ~(SLCR_A9_CPU_CLKSTOP << cpu); 140 zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET); 141 142 zynq_slcr_cpu_state_write(cpu, false); 143 } 144 145 /** 146 * zynq_slcr_cpu_stop - Stop cpu 147 * @cpu: cpu number 148 */ 149 void zynq_slcr_cpu_stop(int cpu) 150 { 151 u32 reg; 152 153 zynq_slcr_read(®, SLCR_A9_CPU_RST_CTRL_OFFSET); 154 reg |= (SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu; 155 zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET); 156 } 157 158 /** 159 * zynq_slcr_cpu_state - Read/write cpu state 160 * @cpu: cpu number 161 * 162 * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1) 163 * 0 means cpu is running, 1 cpu is going to die. 164 * 165 * Return: true if cpu is running, false if cpu is going to die 166 */ 167 bool zynq_slcr_cpu_state_read(int cpu) 168 { 169 u32 state; 170 171 state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET); 172 state &= 1 << (31 - cpu); 173 174 return !state; 175 } 176 177 /** 178 * zynq_slcr_cpu_state - Read/write cpu state 179 * @cpu: cpu number 180 * @die: cpu state - true if cpu is going to die 181 * 182 * SLCR_REBOOT_STATUS save upper 2 bits (31/30 cpu states for cpu0 and cpu1) 183 * 0 means cpu is running, 1 cpu is going to die. 184 */ 185 void zynq_slcr_cpu_state_write(int cpu, bool die) 186 { 187 u32 state, mask; 188 189 state = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET); 190 mask = 1 << (31 - cpu); 191 if (die) 192 state |= mask; 193 else 194 state &= ~mask; 195 writel(state, zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET); 196 } 197 198 /** 199 * zynq_slcr_init - Regular slcr driver init 200 * Return: 0 on success, negative errno otherwise. 201 * 202 * Called early during boot from platform code to remap SLCR area. 203 */ 204 int __init zynq_slcr_init(void) 205 { 206 zynq_slcr_regmap = syscon_regmap_lookup_by_compatible("xlnx,zynq-slcr"); 207 if (IS_ERR(zynq_slcr_regmap)) { 208 pr_err("%s: failed to find zynq-slcr\n", __func__); 209 return -ENODEV; 210 } 211 212 return 0; 213 } 214 215 /** 216 * zynq_early_slcr_init - Early slcr init function 217 * 218 * Return: 0 on success, negative errno otherwise. 219 * 220 * Called very early during boot from platform code to unlock SLCR. 221 */ 222 int __init zynq_early_slcr_init(void) 223 { 224 struct device_node *np; 225 226 np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr"); 227 if (!np) { 228 pr_err("%s: no slcr node found\n", __func__); 229 BUG(); 230 } 231 232 zynq_slcr_base = of_iomap(np, 0); 233 if (!zynq_slcr_base) { 234 pr_err("%s: Unable to map I/O memory\n", __func__); 235 BUG(); 236 } 237 238 np->data = (__force void *)zynq_slcr_base; 239 240 /* unlock the SLCR so that registers can be changed */ 241 zynq_slcr_unlock(); 242 243 pr_info("%s mapped to %p\n", np->name, zynq_slcr_base); 244 245 of_node_put(np); 246 247 return 0; 248 } 249