diff options
Diffstat (limited to 'arch/arm/mach-bcm/platsmp-brcmstb.c')
-rw-r--r-- | arch/arm/mach-bcm/platsmp-brcmstb.c | 85 |
1 files changed, 63 insertions, 22 deletions
diff --git a/arch/arm/mach-bcm/platsmp-brcmstb.c b/arch/arm/mach-bcm/platsmp-brcmstb.c index 31c87a284a34..e209e6fc7caf 100644 --- a/arch/arm/mach-bcm/platsmp-brcmstb.c +++ b/arch/arm/mach-bcm/platsmp-brcmstb.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/errno.h> | 17 | #include <linux/errno.h> |
18 | #include <linux/init.h> | 18 | #include <linux/init.h> |
19 | #include <linux/io.h> | 19 | #include <linux/io.h> |
20 | #include <linux/jiffies.h> | ||
20 | #include <linux/of_address.h> | 21 | #include <linux/of_address.h> |
21 | #include <linux/of_platform.h> | 22 | #include <linux/of_platform.h> |
22 | #include <linux/printk.h> | 23 | #include <linux/printk.h> |
@@ -94,10 +95,35 @@ static u32 pwr_ctrl_rd(u32 cpu) | |||
94 | return readl_relaxed(base); | 95 | return readl_relaxed(base); |
95 | } | 96 | } |
96 | 97 | ||
97 | static void pwr_ctrl_wr(u32 cpu, u32 val) | 98 | static void pwr_ctrl_set(unsigned int cpu, u32 val, u32 mask) |
98 | { | 99 | { |
99 | void __iomem *base = pwr_ctrl_get_base(cpu); | 100 | void __iomem *base = pwr_ctrl_get_base(cpu); |
100 | writel(val, base); | 101 | writel((readl(base) & mask) | val, base); |
102 | } | ||
103 | |||
104 | static void pwr_ctrl_clr(unsigned int cpu, u32 val, u32 mask) | ||
105 | { | ||
106 | void __iomem *base = pwr_ctrl_get_base(cpu); | ||
107 | writel((readl(base) & mask) & ~val, base); | ||
108 | } | ||
109 | |||
110 | #define POLL_TMOUT_MS 500 | ||
111 | static int pwr_ctrl_wait_tmout(unsigned int cpu, u32 set, u32 mask) | ||
112 | { | ||
113 | const unsigned long timeo = jiffies + msecs_to_jiffies(POLL_TMOUT_MS); | ||
114 | u32 tmp; | ||
115 | |||
116 | do { | ||
117 | tmp = pwr_ctrl_rd(cpu) & mask; | ||
118 | if (!set == !tmp) | ||
119 | return 0; | ||
120 | } while (time_before(jiffies, timeo)); | ||
121 | |||
122 | tmp = pwr_ctrl_rd(cpu) & mask; | ||
123 | if (!set == !tmp) | ||
124 | return 0; | ||
125 | |||
126 | return -ETIMEDOUT; | ||
101 | } | 127 | } |
102 | 128 | ||
103 | static void cpu_rst_cfg_set(u32 cpu, int set) | 129 | static void cpu_rst_cfg_set(u32 cpu, int set) |
@@ -139,15 +165,22 @@ static void brcmstb_cpu_power_on(u32 cpu) | |||
139 | * The secondary cores power was cut, so we must go through | 165 | * The secondary cores power was cut, so we must go through |
140 | * power-on initialization. | 166 | * power-on initialization. |
141 | */ | 167 | */ |
142 | u32 tmp; | 168 | pwr_ctrl_set(cpu, ZONE_MAN_ISO_CNTL_MASK, 0xffffff00); |
169 | pwr_ctrl_set(cpu, ZONE_MANUAL_CONTROL_MASK, -1); | ||
170 | pwr_ctrl_set(cpu, ZONE_RESERVED_1_MASK, -1); | ||
143 | 171 | ||
144 | /* Request zone power up */ | 172 | pwr_ctrl_set(cpu, ZONE_MAN_MEM_PWR_MASK, -1); |
145 | pwr_ctrl_wr(cpu, ZONE_PWR_UP_REQ_MASK); | ||
146 | 173 | ||
147 | /* Wait for the power up FSM to complete */ | 174 | if (pwr_ctrl_wait_tmout(cpu, 1, ZONE_MEM_PWR_STATE_MASK)) |
148 | do { | 175 | panic("ZONE_MEM_PWR_STATE_MASK set timeout"); |
149 | tmp = pwr_ctrl_rd(cpu); | 176 | |
150 | } while (!(tmp & ZONE_PWR_ON_STATE_MASK)); | 177 | pwr_ctrl_set(cpu, ZONE_MAN_CLKEN_MASK, -1); |
178 | |||
179 | if (pwr_ctrl_wait_tmout(cpu, 1, ZONE_DPG_PWR_STATE_MASK)) | ||
180 | panic("ZONE_DPG_PWR_STATE_MASK set timeout"); | ||
181 | |||
182 | pwr_ctrl_clr(cpu, ZONE_MAN_ISO_CNTL_MASK, -1); | ||
183 | pwr_ctrl_set(cpu, ZONE_MAN_RESET_CNTL_MASK, -1); | ||
151 | } | 184 | } |
152 | 185 | ||
153 | static int brcmstb_cpu_get_power_state(u32 cpu) | 186 | static int brcmstb_cpu_get_power_state(u32 cpu) |
@@ -174,25 +207,33 @@ static void brcmstb_cpu_die(u32 cpu) | |||
174 | 207 | ||
175 | static int brcmstb_cpu_kill(u32 cpu) | 208 | static int brcmstb_cpu_kill(u32 cpu) |
176 | { | 209 | { |
177 | u32 tmp; | 210 | /* |
211 | * Ordinarily, the hardware forbids power-down of CPU0 (which is good | ||
212 | * because it is the boot CPU), but this is not true when using BPCM | ||
213 | * manual mode. Consequently, we must avoid turning off CPU0 here to | ||
214 | * ensure that TI2C master reset will work. | ||
215 | */ | ||
216 | if (cpu == 0) { | ||
217 | pr_warn("SMP: refusing to power off CPU0\n"); | ||
218 | return 1; | ||
219 | } | ||
178 | 220 | ||
179 | while (per_cpu_sw_state_rd(cpu)) | 221 | while (per_cpu_sw_state_rd(cpu)) |
180 | ; | 222 | ; |
181 | 223 | ||
182 | /* Program zone reset */ | 224 | pwr_ctrl_set(cpu, ZONE_MANUAL_CONTROL_MASK, -1); |
183 | pwr_ctrl_wr(cpu, ZONE_RESET_STATE_MASK | ZONE_BLK_RST_ASSERT_MASK | | 225 | pwr_ctrl_clr(cpu, ZONE_MAN_RESET_CNTL_MASK, -1); |
184 | ZONE_PWR_DN_REQ_MASK); | 226 | pwr_ctrl_clr(cpu, ZONE_MAN_CLKEN_MASK, -1); |
227 | pwr_ctrl_set(cpu, ZONE_MAN_ISO_CNTL_MASK, -1); | ||
228 | pwr_ctrl_clr(cpu, ZONE_MAN_MEM_PWR_MASK, -1); | ||
185 | 229 | ||
186 | /* Verify zone reset */ | 230 | if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_MEM_PWR_STATE_MASK)) |
187 | tmp = pwr_ctrl_rd(cpu); | 231 | panic("ZONE_MEM_PWR_STATE_MASK clear timeout"); |
188 | if (!(tmp & ZONE_RESET_STATE_MASK)) | ||
189 | pr_err("%s: Zone reset bit for CPU %d not asserted!\n", | ||
190 | __func__, cpu); | ||
191 | 232 | ||
192 | /* Wait for power down */ | 233 | pwr_ctrl_clr(cpu, ZONE_RESERVED_1_MASK, -1); |
193 | do { | 234 | |
194 | tmp = pwr_ctrl_rd(cpu); | 235 | if (pwr_ctrl_wait_tmout(cpu, 0, ZONE_DPG_PWR_STATE_MASK)) |
195 | } while (!(tmp & ZONE_PWR_OFF_STATE_MASK)); | 236 | panic("ZONE_DPG_PWR_STATE_MASK clear timeout"); |
196 | 237 | ||
197 | /* Flush pipeline before resetting CPU */ | 238 | /* Flush pipeline before resetting CPU */ |
198 | mb(); | 239 | mb(); |