diff options
Diffstat (limited to 'arch/arm/mach-zynq/slcr.c')
-rw-r--r-- | arch/arm/mach-zynq/slcr.c | 104 |
1 files changed, 89 insertions, 15 deletions
diff --git a/arch/arm/mach-zynq/slcr.c b/arch/arm/mach-zynq/slcr.c index 1836d5a34606..a37d49a6e657 100644 --- a/arch/arm/mach-zynq/slcr.c +++ b/arch/arm/mach-zynq/slcr.c | |||
@@ -15,7 +15,9 @@ | |||
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include <linux/io.h> | 17 | #include <linux/io.h> |
18 | #include <linux/mfd/syscon.h> | ||
18 | #include <linux/of_address.h> | 19 | #include <linux/of_address.h> |
20 | #include <linux/regmap.h> | ||
19 | #include <linux/clk/zynq.h> | 21 | #include <linux/clk/zynq.h> |
20 | #include "common.h" | 22 | #include "common.h" |
21 | 23 | ||
@@ -29,7 +31,56 @@ | |||
29 | #define SLCR_A9_CPU_CLKSTOP 0x10 | 31 | #define SLCR_A9_CPU_CLKSTOP 0x10 |
30 | #define SLCR_A9_CPU_RST 0x1 | 32 | #define SLCR_A9_CPU_RST 0x1 |
31 | 33 | ||
32 | void __iomem *zynq_slcr_base; | 34 | static void __iomem *zynq_slcr_base; |
35 | static struct regmap *zynq_slcr_regmap; | ||
36 | |||
37 | /** | ||
38 | * zynq_slcr_write - Write to a register in SLCR block | ||
39 | * | ||
40 | * @val: Value to write to the register | ||
41 | * @offset: Register offset in SLCR block | ||
42 | * | ||
43 | * Return: a negative value on error, 0 on success | ||
44 | */ | ||
45 | static int zynq_slcr_write(u32 val, u32 offset) | ||
46 | { | ||
47 | if (!zynq_slcr_regmap) { | ||
48 | writel(val, zynq_slcr_base + offset); | ||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | return regmap_write(zynq_slcr_regmap, offset, val); | ||
53 | } | ||
54 | |||
55 | /** | ||
56 | * zynq_slcr_read - Read a register in SLCR block | ||
57 | * | ||
58 | * @val: Pointer to value to be read from SLCR | ||
59 | * @offset: Register offset in SLCR block | ||
60 | * | ||
61 | * Return: a negative value on error, 0 on success | ||
62 | */ | ||
63 | static int zynq_slcr_read(u32 *val, u32 offset) | ||
64 | { | ||
65 | if (zynq_slcr_regmap) | ||
66 | return regmap_read(zynq_slcr_regmap, offset, val); | ||
67 | |||
68 | *val = readl(zynq_slcr_base + offset); | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * zynq_slcr_unlock - Unlock SLCR registers | ||
75 | * | ||
76 | * Return: a negative value on error, 0 on success | ||
77 | */ | ||
78 | static inline int zynq_slcr_unlock(void) | ||
79 | { | ||
80 | zynq_slcr_write(SLCR_UNLOCK_MAGIC, SLCR_UNLOCK_OFFSET); | ||
81 | |||
82 | return 0; | ||
83 | } | ||
33 | 84 | ||
34 | /** | 85 | /** |
35 | * zynq_slcr_system_reset - Reset the entire system. | 86 | * zynq_slcr_system_reset - Reset the entire system. |
@@ -43,16 +94,16 @@ void zynq_slcr_system_reset(void) | |||
43 | * Note that this seems to require raw i/o | 94 | * Note that this seems to require raw i/o |
44 | * functions or there's a lockup? | 95 | * functions or there's a lockup? |
45 | */ | 96 | */ |
46 | writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK_OFFSET); | 97 | zynq_slcr_unlock(); |
47 | 98 | ||
48 | /* | 99 | /* |
49 | * Clear 0x0F000000 bits of reboot status register to workaround | 100 | * Clear 0x0F000000 bits of reboot status register to workaround |
50 | * the FSBL not loading the bitstream after soft-reboot | 101 | * the FSBL not loading the bitstream after soft-reboot |
51 | * This is a temporary solution until we know more. | 102 | * This is a temporary solution until we know more. |
52 | */ | 103 | */ |
53 | reboot = readl(zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET); | 104 | zynq_slcr_read(&reboot, SLCR_REBOOT_STATUS_OFFSET); |
54 | writel(reboot & 0xF0FFFFFF, zynq_slcr_base + SLCR_REBOOT_STATUS_OFFSET); | 105 | zynq_slcr_write(reboot & 0xF0FFFFFF, SLCR_REBOOT_STATUS_OFFSET); |
55 | writel(1, zynq_slcr_base + SLCR_PS_RST_CTRL_OFFSET); | 106 | zynq_slcr_write(1, SLCR_PS_RST_CTRL_OFFSET); |
56 | } | 107 | } |
57 | 108 | ||
58 | /** | 109 | /** |
@@ -61,11 +112,13 @@ void zynq_slcr_system_reset(void) | |||
61 | */ | 112 | */ |
62 | void zynq_slcr_cpu_start(int cpu) | 113 | void zynq_slcr_cpu_start(int cpu) |
63 | { | 114 | { |
64 | u32 reg = readl(zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET); | 115 | u32 reg; |
116 | |||
117 | zynq_slcr_read(®, SLCR_A9_CPU_RST_CTRL_OFFSET); | ||
65 | reg &= ~(SLCR_A9_CPU_RST << cpu); | 118 | reg &= ~(SLCR_A9_CPU_RST << cpu); |
66 | writel(reg, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET); | 119 | zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET); |
67 | reg &= ~(SLCR_A9_CPU_CLKSTOP << cpu); | 120 | reg &= ~(SLCR_A9_CPU_CLKSTOP << cpu); |
68 | writel(reg, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET); | 121 | zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET); |
69 | } | 122 | } |
70 | 123 | ||
71 | /** | 124 | /** |
@@ -74,19 +127,40 @@ void zynq_slcr_cpu_start(int cpu) | |||
74 | */ | 127 | */ |
75 | void zynq_slcr_cpu_stop(int cpu) | 128 | void zynq_slcr_cpu_stop(int cpu) |
76 | { | 129 | { |
77 | u32 reg = readl(zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET); | 130 | u32 reg; |
131 | |||
132 | zynq_slcr_read(®, SLCR_A9_CPU_RST_CTRL_OFFSET); | ||
78 | reg |= (SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu; | 133 | reg |= (SLCR_A9_CPU_CLKSTOP | SLCR_A9_CPU_RST) << cpu; |
79 | writel(reg, zynq_slcr_base + SLCR_A9_CPU_RST_CTRL_OFFSET); | 134 | zynq_slcr_write(reg, SLCR_A9_CPU_RST_CTRL_OFFSET); |
80 | } | 135 | } |
81 | 136 | ||
82 | /** | 137 | /** |
83 | * zynq_slcr_init | 138 | * zynq_slcr_init - Regular slcr driver init |
84 | * Returns 0 on success, negative errno otherwise. | 139 | * |
140 | * Return: 0 on success, negative errno otherwise. | ||
85 | * | 141 | * |
86 | * Called early during boot from platform code to remap SLCR area. | 142 | * Called early during boot from platform code to remap SLCR area. |
87 | */ | 143 | */ |
88 | int __init zynq_slcr_init(void) | 144 | int __init zynq_slcr_init(void) |
89 | { | 145 | { |
146 | zynq_slcr_regmap = syscon_regmap_lookup_by_compatible("xlnx,zynq-slcr"); | ||
147 | if (IS_ERR(zynq_slcr_regmap)) { | ||
148 | pr_err("%s: failed to find zynq-slcr\n", __func__); | ||
149 | return -ENODEV; | ||
150 | } | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | /** | ||
156 | * zynq_early_slcr_init - Early slcr init function | ||
157 | * | ||
158 | * Return: 0 on success, negative errno otherwise. | ||
159 | * | ||
160 | * Called very early during boot from platform code to unlock SLCR. | ||
161 | */ | ||
162 | int __init zynq_early_slcr_init(void) | ||
163 | { | ||
90 | struct device_node *np; | 164 | struct device_node *np; |
91 | 165 | ||
92 | np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr"); | 166 | np = of_find_compatible_node(NULL, NULL, "xlnx,zynq-slcr"); |
@@ -101,13 +175,13 @@ int __init zynq_slcr_init(void) | |||
101 | BUG(); | 175 | BUG(); |
102 | } | 176 | } |
103 | 177 | ||
178 | np->data = (__force void *)zynq_slcr_base; | ||
179 | |||
104 | /* unlock the SLCR so that registers can be changed */ | 180 | /* unlock the SLCR so that registers can be changed */ |
105 | writel(SLCR_UNLOCK_MAGIC, zynq_slcr_base + SLCR_UNLOCK_OFFSET); | 181 | zynq_slcr_unlock(); |
106 | 182 | ||
107 | pr_info("%s mapped to %p\n", np->name, zynq_slcr_base); | 183 | pr_info("%s mapped to %p\n", np->name, zynq_slcr_base); |
108 | 184 | ||
109 | zynq_clock_init(zynq_slcr_base); | ||
110 | |||
111 | of_node_put(np); | 185 | of_node_put(np); |
112 | 186 | ||
113 | return 0; | 187 | return 0; |