diff options
| -rw-r--r-- | drivers/soc/bcm/brcmstb/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/soc/bcm/brcmstb/pm/Makefile | 1 | ||||
| -rw-r--r-- | drivers/soc/bcm/brcmstb/pm/pm-mips.c | 461 | ||||
| -rw-r--r-- | drivers/soc/bcm/brcmstb/pm/pm.h | 13 | ||||
| -rw-r--r-- | drivers/soc/bcm/brcmstb/pm/s2-mips.S | 200 | ||||
| -rw-r--r-- | drivers/soc/bcm/brcmstb/pm/s3-mips.S | 146 |
6 files changed, 821 insertions, 2 deletions
diff --git a/drivers/soc/bcm/brcmstb/Kconfig b/drivers/soc/bcm/brcmstb/Kconfig index 4425430119fe..d36f6e03c1a6 100644 --- a/drivers/soc/bcm/brcmstb/Kconfig +++ b/drivers/soc/bcm/brcmstb/Kconfig | |||
| @@ -4,7 +4,7 @@ config BRCMSTB_PM | |||
| 4 | bool "Support suspend/resume for STB platforms" | 4 | bool "Support suspend/resume for STB platforms" |
| 5 | default y | 5 | default y |
| 6 | depends on PM | 6 | depends on PM |
| 7 | depends on ARCH_BRCMSTB | 7 | depends on ARCH_BRCMSTB || BMIPS_GENERIC |
| 8 | select ARM_CPU_SUSPEND if ARM | 8 | select ARM_CPU_SUSPEND if ARM |
| 9 | 9 | ||
| 10 | endif # SOC_BRCMSTB | 10 | endif # SOC_BRCMSTB |
diff --git a/drivers/soc/bcm/brcmstb/pm/Makefile b/drivers/soc/bcm/brcmstb/pm/Makefile index 7c3d20135b7c..08bbd244ef11 100644 --- a/drivers/soc/bcm/brcmstb/pm/Makefile +++ b/drivers/soc/bcm/brcmstb/pm/Makefile | |||
| @@ -1,2 +1,3 @@ | |||
| 1 | obj-$(CONFIG_ARM) += s2-arm.o pm-arm.o | 1 | obj-$(CONFIG_ARM) += s2-arm.o pm-arm.o |
| 2 | AFLAGS_s2-arm.o := -march=armv7-a | 2 | AFLAGS_s2-arm.o := -march=armv7-a |
| 3 | obj-$(CONFIG_BMIPS_GENERIC) += s2-mips.o s3-mips.o pm-mips.o | ||
diff --git a/drivers/soc/bcm/brcmstb/pm/pm-mips.c b/drivers/soc/bcm/brcmstb/pm/pm-mips.c new file mode 100644 index 000000000000..9300b5f62e56 --- /dev/null +++ b/drivers/soc/bcm/brcmstb/pm/pm-mips.c | |||
| @@ -0,0 +1,461 @@ | |||
| 1 | /* | ||
| 2 | * MIPS-specific support for Broadcom STB S2/S3/S5 power management | ||
| 3 | * | ||
| 4 | * Copyright (C) 2016-2017 Broadcom | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License version 2 as | ||
| 8 | * published by the Free Software Foundation. | ||
| 9 | * | ||
| 10 | * This program is distributed in the hope that it will be useful, | ||
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 13 | * GNU General Public License for more details. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #include <linux/kernel.h> | ||
| 17 | #include <linux/printk.h> | ||
| 18 | #include <linux/io.h> | ||
| 19 | #include <linux/of.h> | ||
| 20 | #include <linux/of_address.h> | ||
| 21 | #include <linux/delay.h> | ||
| 22 | #include <linux/suspend.h> | ||
| 23 | #include <asm/bmips.h> | ||
| 24 | #include <asm/tlbflush.h> | ||
| 25 | |||
| 26 | #include "pm.h" | ||
| 27 | |||
| 28 | #define S2_NUM_PARAMS 6 | ||
| 29 | #define MAX_NUM_MEMC 3 | ||
| 30 | |||
| 31 | /* S3 constants */ | ||
| 32 | #define MAX_GP_REGS 16 | ||
| 33 | #define MAX_CP0_REGS 32 | ||
| 34 | #define NUM_MEMC_CLIENTS 128 | ||
| 35 | #define AON_CTRL_RAM_SIZE 128 | ||
| 36 | #define BRCMSTB_S3_MAGIC 0x5AFEB007 | ||
| 37 | |||
| 38 | #define CLEAR_RESET_MASK 0x01 | ||
| 39 | |||
| 40 | /* Index each CP0 register that needs to be saved */ | ||
| 41 | #define CONTEXT 0 | ||
| 42 | #define USER_LOCAL 1 | ||
| 43 | #define PGMK 2 | ||
| 44 | #define HWRENA 3 | ||
| 45 | #define COMPARE 4 | ||
| 46 | #define STATUS 5 | ||
| 47 | #define CONFIG 6 | ||
| 48 | #define MODE 7 | ||
| 49 | #define EDSP 8 | ||
| 50 | #define BOOT_VEC 9 | ||
| 51 | #define EBASE 10 | ||
| 52 | |||
| 53 | struct brcmstb_memc { | ||
| 54 | void __iomem *ddr_phy_base; | ||
| 55 | void __iomem *arb_base; | ||
| 56 | }; | ||
| 57 | |||
| 58 | struct brcmstb_pm_control { | ||
| 59 | void __iomem *aon_ctrl_base; | ||
| 60 | void __iomem *aon_sram_base; | ||
| 61 | void __iomem *timers_base; | ||
| 62 | struct brcmstb_memc memcs[MAX_NUM_MEMC]; | ||
| 63 | int num_memc; | ||
| 64 | }; | ||
| 65 | |||
| 66 | struct brcm_pm_s3_context { | ||
| 67 | u32 cp0_regs[MAX_CP0_REGS]; | ||
| 68 | u32 memc0_rts[NUM_MEMC_CLIENTS]; | ||
| 69 | u32 sc_boot_vec; | ||
| 70 | }; | ||
| 71 | |||
| 72 | struct brcmstb_mem_transfer; | ||
| 73 | |||
| 74 | struct brcmstb_mem_transfer { | ||
| 75 | struct brcmstb_mem_transfer *next; | ||
| 76 | void *src; | ||
| 77 | void *dst; | ||
| 78 | dma_addr_t pa_src; | ||
| 79 | dma_addr_t pa_dst; | ||
| 80 | u32 len; | ||
| 81 | u8 key; | ||
| 82 | u8 mode; | ||
| 83 | u8 src_remapped; | ||
| 84 | u8 dst_remapped; | ||
| 85 | u8 src_dst_remapped; | ||
| 86 | }; | ||
| 87 | |||
| 88 | #define AON_SAVE_SRAM(base, idx, val) \ | ||
| 89 | __raw_writel(val, base + (idx << 2)) | ||
| 90 | |||
| 91 | /* Used for saving registers in asm */ | ||
| 92 | u32 gp_regs[MAX_GP_REGS]; | ||
| 93 | |||
| 94 | #define BSP_CLOCK_STOP 0x00 | ||
| 95 | #define PM_INITIATE 0x01 | ||
| 96 | |||
| 97 | static struct brcmstb_pm_control ctrl; | ||
| 98 | |||
| 99 | static void brcm_pm_save_cp0_context(struct brcm_pm_s3_context *ctx) | ||
| 100 | { | ||
| 101 | /* Generic MIPS */ | ||
| 102 | ctx->cp0_regs[CONTEXT] = read_c0_context(); | ||
| 103 | ctx->cp0_regs[USER_LOCAL] = read_c0_userlocal(); | ||
| 104 | ctx->cp0_regs[PGMK] = read_c0_pagemask(); | ||
| 105 | ctx->cp0_regs[HWRENA] = read_c0_cache(); | ||
| 106 | ctx->cp0_regs[COMPARE] = read_c0_compare(); | ||
| 107 | ctx->cp0_regs[STATUS] = read_c0_status(); | ||
| 108 | |||
| 109 | /* Broadcom specific */ | ||
| 110 | ctx->cp0_regs[CONFIG] = read_c0_brcm_config(); | ||
| 111 | ctx->cp0_regs[MODE] = read_c0_brcm_mode(); | ||
| 112 | ctx->cp0_regs[EDSP] = read_c0_brcm_edsp(); | ||
| 113 | ctx->cp0_regs[BOOT_VEC] = read_c0_brcm_bootvec(); | ||
| 114 | ctx->cp0_regs[EBASE] = read_c0_ebase(); | ||
| 115 | |||
| 116 | ctx->sc_boot_vec = bmips_read_zscm_reg(0xa0); | ||
| 117 | } | ||
| 118 | |||
| 119 | static void brcm_pm_restore_cp0_context(struct brcm_pm_s3_context *ctx) | ||
| 120 | { | ||
| 121 | /* Restore cp0 state */ | ||
| 122 | bmips_write_zscm_reg(0xa0, ctx->sc_boot_vec); | ||
| 123 | |||
| 124 | /* Generic MIPS */ | ||
| 125 | write_c0_context(ctx->cp0_regs[CONTEXT]); | ||
| 126 | write_c0_userlocal(ctx->cp0_regs[USER_LOCAL]); | ||
| 127 | write_c0_pagemask(ctx->cp0_regs[PGMK]); | ||
| 128 | write_c0_cache(ctx->cp0_regs[HWRENA]); | ||
| 129 | write_c0_compare(ctx->cp0_regs[COMPARE]); | ||
| 130 | write_c0_status(ctx->cp0_regs[STATUS]); | ||
| 131 | |||
| 132 | /* Broadcom specific */ | ||
| 133 | write_c0_brcm_config(ctx->cp0_regs[CONFIG]); | ||
| 134 | write_c0_brcm_mode(ctx->cp0_regs[MODE]); | ||
| 135 | write_c0_brcm_edsp(ctx->cp0_regs[EDSP]); | ||
| 136 | write_c0_brcm_bootvec(ctx->cp0_regs[BOOT_VEC]); | ||
| 137 | write_c0_ebase(ctx->cp0_regs[EBASE]); | ||
| 138 | } | ||
| 139 | |||
| 140 | static void brcmstb_pm_handshake(void) | ||
| 141 | { | ||
| 142 | void __iomem *base = ctrl.aon_ctrl_base; | ||
| 143 | u32 tmp; | ||
| 144 | |||
| 145 | /* BSP power handshake, v1 */ | ||
| 146 | tmp = __raw_readl(base + AON_CTRL_HOST_MISC_CMDS); | ||
| 147 | tmp &= ~1UL; | ||
| 148 | __raw_writel(tmp, base + AON_CTRL_HOST_MISC_CMDS); | ||
| 149 | (void)__raw_readl(base + AON_CTRL_HOST_MISC_CMDS); | ||
| 150 | |||
| 151 | __raw_writel(0, base + AON_CTRL_PM_INITIATE); | ||
| 152 | (void)__raw_readl(base + AON_CTRL_PM_INITIATE); | ||
| 153 | __raw_writel(BSP_CLOCK_STOP | PM_INITIATE, | ||
| 154 | base + AON_CTRL_PM_INITIATE); | ||
| 155 | /* | ||
| 156 | * HACK: BSP may have internal race on the CLOCK_STOP command. | ||
| 157 | * Avoid touching the BSP for a few milliseconds. | ||
| 158 | */ | ||
| 159 | mdelay(3); | ||
| 160 | } | ||
| 161 | |||
| 162 | static void brcmstb_pm_s5(void) | ||
| 163 | { | ||
| 164 | void __iomem *base = ctrl.aon_ctrl_base; | ||
| 165 | |||
| 166 | brcmstb_pm_handshake(); | ||
| 167 | |||
| 168 | /* Clear magic s3 warm-boot value */ | ||
| 169 | AON_SAVE_SRAM(ctrl.aon_sram_base, 0, 0); | ||
| 170 | |||
| 171 | /* Set the countdown */ | ||
| 172 | __raw_writel(0x10, base + AON_CTRL_PM_CPU_WAIT_COUNT); | ||
| 173 | (void)__raw_readl(base + AON_CTRL_PM_CPU_WAIT_COUNT); | ||
| 174 | |||
| 175 | /* Prepare to S5 cold boot */ | ||
| 176 | __raw_writel(PM_COLD_CONFIG, base + AON_CTRL_PM_CTRL); | ||
| 177 | (void)__raw_readl(base + AON_CTRL_PM_CTRL); | ||
| 178 | |||
| 179 | __raw_writel((PM_COLD_CONFIG | PM_PWR_DOWN), base + | ||
| 180 | AON_CTRL_PM_CTRL); | ||
| 181 | (void)__raw_readl(base + AON_CTRL_PM_CTRL); | ||
| 182 | |||
| 183 | __asm__ __volatile__( | ||
| 184 | " wait\n" | ||
| 185 | : : : "memory"); | ||
| 186 | } | ||
| 187 | |||
| 188 | static int brcmstb_pm_s3(void) | ||
| 189 | { | ||
| 190 | struct brcm_pm_s3_context s3_context; | ||
| 191 | void __iomem *memc_arb_base; | ||
| 192 | unsigned long flags; | ||
| 193 | u32 tmp; | ||
| 194 | int i; | ||
| 195 | |||
| 196 | /* Prepare for s3 */ | ||
| 197 | AON_SAVE_SRAM(ctrl.aon_sram_base, 0, BRCMSTB_S3_MAGIC); | ||
| 198 | AON_SAVE_SRAM(ctrl.aon_sram_base, 1, (u32)&s3_reentry); | ||
| 199 | AON_SAVE_SRAM(ctrl.aon_sram_base, 2, 0); | ||
| 200 | |||
| 201 | /* Clear RESET_HISTORY */ | ||
| 202 | tmp = __raw_readl(ctrl.aon_ctrl_base + AON_CTRL_RESET_CTRL); | ||
| 203 | tmp &= ~CLEAR_RESET_MASK; | ||
| 204 | __raw_writel(tmp, ctrl.aon_ctrl_base + AON_CTRL_RESET_CTRL); | ||
| 205 | |||
| 206 | local_irq_save(flags); | ||
| 207 | |||
| 208 | /* Inhibit DDR_RSTb pulse for both MMCs*/ | ||
| 209 | for (i = 0; i < ctrl.num_memc; i++) { | ||
| 210 | tmp = __raw_readl(ctrl.memcs[i].ddr_phy_base + | ||
| 211 | DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL); | ||
| 212 | |||
| 213 | tmp &= ~0x0f; | ||
| 214 | __raw_writel(tmp, ctrl.memcs[i].ddr_phy_base + | ||
| 215 | DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL); | ||
| 216 | tmp |= (0x05 | BIT(5)); | ||
| 217 | __raw_writel(tmp, ctrl.memcs[i].ddr_phy_base + | ||
| 218 | DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL); | ||
| 219 | } | ||
| 220 | |||
| 221 | /* Save CP0 context */ | ||
| 222 | brcm_pm_save_cp0_context(&s3_context); | ||
| 223 | |||
| 224 | /* Save RTS(skip debug register) */ | ||
| 225 | memc_arb_base = ctrl.memcs[0].arb_base + 4; | ||
| 226 | for (i = 0; i < NUM_MEMC_CLIENTS; i++) { | ||
| 227 | s3_context.memc0_rts[i] = __raw_readl(memc_arb_base); | ||
| 228 | memc_arb_base += 4; | ||
| 229 | } | ||
| 230 | |||
| 231 | /* Save I/O context */ | ||
| 232 | local_flush_tlb_all(); | ||
| 233 | _dma_cache_wback_inv(0, ~0); | ||
| 234 | |||
| 235 | brcm_pm_do_s3(ctrl.aon_ctrl_base, current_cpu_data.dcache.linesz); | ||
| 236 | |||
| 237 | /* CPU reconfiguration */ | ||
| 238 | local_flush_tlb_all(); | ||
| 239 | bmips_cpu_setup(); | ||
| 240 | cpumask_clear(&bmips_booted_mask); | ||
| 241 | |||
| 242 | /* Restore RTS (skip debug register) */ | ||
| 243 | memc_arb_base = ctrl.memcs[0].arb_base + 4; | ||
| 244 | for (i = 0; i < NUM_MEMC_CLIENTS; i++) { | ||
| 245 | __raw_writel(s3_context.memc0_rts[i], memc_arb_base); | ||
| 246 | memc_arb_base += 4; | ||
| 247 | } | ||
| 248 | |||
| 249 | /* restore CP0 context */ | ||
| 250 | brcm_pm_restore_cp0_context(&s3_context); | ||
| 251 | |||
| 252 | local_irq_restore(flags); | ||
| 253 | |||
| 254 | return 0; | ||
| 255 | } | ||
| 256 | |||
| 257 | static int brcmstb_pm_s2(void) | ||
| 258 | { | ||
| 259 | /* | ||
| 260 | * We need to pass 6 arguments to an assembly function. Lets avoid the | ||
| 261 | * stack and pass arguments in a explicit 4 byte array. The assembly | ||
| 262 | * code assumes all arguments are 4 bytes and arguments are ordered | ||
| 263 | * like so: | ||
| 264 | * | ||
| 265 | * 0: AON_CTRl base register | ||
| 266 | * 1: DDR_PHY base register | ||
| 267 | * 2: TIMERS base resgister | ||
| 268 | * 3: I-Cache line size | ||
| 269 | * 4: Restart vector address | ||
| 270 | * 5: Restart vector size | ||
| 271 | */ | ||
| 272 | u32 s2_params[6]; | ||
| 273 | |||
| 274 | /* Prepare s2 parameters */ | ||
| 275 | s2_params[0] = (u32)ctrl.aon_ctrl_base; | ||
| 276 | s2_params[1] = (u32)ctrl.memcs[0].ddr_phy_base; | ||
| 277 | s2_params[2] = (u32)ctrl.timers_base; | ||
| 278 | s2_params[3] = (u32)current_cpu_data.icache.linesz; | ||
| 279 | s2_params[4] = (u32)BMIPS_WARM_RESTART_VEC; | ||
| 280 | s2_params[5] = (u32)(bmips_smp_int_vec_end - | ||
| 281 | bmips_smp_int_vec); | ||
| 282 | |||
| 283 | /* Drop to standby */ | ||
| 284 | brcm_pm_do_s2(s2_params); | ||
| 285 | |||
| 286 | return 0; | ||
| 287 | } | ||
| 288 | |||
| 289 | static int brcmstb_pm_standby(bool deep_standby) | ||
| 290 | { | ||
| 291 | brcmstb_pm_handshake(); | ||
| 292 | |||
| 293 | /* Send IRQs to BMIPS_WARM_RESTART_VEC */ | ||
| 294 | clear_c0_cause(CAUSEF_IV); | ||
| 295 | irq_disable_hazard(); | ||
| 296 | set_c0_status(ST0_BEV); | ||
| 297 | irq_disable_hazard(); | ||
| 298 | |||
| 299 | if (deep_standby) | ||
| 300 | brcmstb_pm_s3(); | ||
| 301 | else | ||
| 302 | brcmstb_pm_s2(); | ||
| 303 | |||
| 304 | /* Send IRQs to normal runtime vectors */ | ||
| 305 | clear_c0_status(ST0_BEV); | ||
| 306 | irq_disable_hazard(); | ||
| 307 | set_c0_cause(CAUSEF_IV); | ||
| 308 | irq_disable_hazard(); | ||
| 309 | |||
| 310 | return 0; | ||
| 311 | } | ||
| 312 | |||
| 313 | static int brcmstb_pm_enter(suspend_state_t state) | ||
| 314 | { | ||
| 315 | int ret = -EINVAL; | ||
| 316 | |||
| 317 | switch (state) { | ||
| 318 | case PM_SUSPEND_STANDBY: | ||
| 319 | ret = brcmstb_pm_standby(false); | ||
| 320 | break; | ||
| 321 | case PM_SUSPEND_MEM: | ||
| 322 | ret = brcmstb_pm_standby(true); | ||
| 323 | break; | ||
| 324 | } | ||
| 325 | |||
| 326 | return ret; | ||
| 327 | } | ||
| 328 | |||
| 329 | static int brcmstb_pm_valid(suspend_state_t state) | ||
| 330 | { | ||
| 331 | switch (state) { | ||
| 332 | case PM_SUSPEND_STANDBY: | ||
| 333 | return true; | ||
| 334 | case PM_SUSPEND_MEM: | ||
| 335 | return true; | ||
| 336 | default: | ||
| 337 | return false; | ||
| 338 | } | ||
| 339 | } | ||
| 340 | |||
| 341 | static const struct platform_suspend_ops brcmstb_pm_ops = { | ||
| 342 | .enter = brcmstb_pm_enter, | ||
| 343 | .valid = brcmstb_pm_valid, | ||
| 344 | }; | ||
| 345 | |||
| 346 | static const struct of_device_id aon_ctrl_dt_ids[] = { | ||
| 347 | { .compatible = "brcm,brcmstb-aon-ctrl" }, | ||
| 348 | { /* sentinel */ } | ||
| 349 | }; | ||
| 350 | |||
| 351 | static const struct of_device_id ddr_phy_dt_ids[] = { | ||
| 352 | { .compatible = "brcm,brcmstb-ddr-phy" }, | ||
| 353 | { /* sentinel */ } | ||
| 354 | }; | ||
| 355 | |||
| 356 | static const struct of_device_id arb_dt_ids[] = { | ||
| 357 | { .compatible = "brcm,brcmstb-memc-arb" }, | ||
| 358 | { /* sentinel */ } | ||
| 359 | }; | ||
| 360 | |||
| 361 | static const struct of_device_id timers_ids[] = { | ||
| 362 | { .compatible = "brcm,brcmstb-timers" }, | ||
| 363 | { /* sentinel */ } | ||
| 364 | }; | ||
| 365 | |||
| 366 | static inline void __iomem *brcmstb_ioremap_node(struct device_node *dn, | ||
| 367 | int index) | ||
| 368 | { | ||
| 369 | return of_io_request_and_map(dn, index, dn->full_name); | ||
| 370 | } | ||
| 371 | |||
| 372 | static void __iomem *brcmstb_ioremap_match(const struct of_device_id *matches, | ||
| 373 | int index, const void **ofdata) | ||
| 374 | { | ||
| 375 | struct device_node *dn; | ||
| 376 | const struct of_device_id *match; | ||
| 377 | |||
| 378 | dn = of_find_matching_node_and_match(NULL, matches, &match); | ||
| 379 | if (!dn) | ||
| 380 | return ERR_PTR(-EINVAL); | ||
| 381 | |||
| 382 | if (ofdata) | ||
| 383 | *ofdata = match->data; | ||
| 384 | |||
| 385 | return brcmstb_ioremap_node(dn, index); | ||
| 386 | } | ||
| 387 | |||
| 388 | static int brcmstb_pm_init(void) | ||
| 389 | { | ||
| 390 | struct device_node *dn; | ||
| 391 | void __iomem *base; | ||
| 392 | int i; | ||
| 393 | |||
| 394 | /* AON ctrl registers */ | ||
| 395 | base = brcmstb_ioremap_match(aon_ctrl_dt_ids, 0, NULL); | ||
| 396 | if (IS_ERR(base)) { | ||
| 397 | pr_err("error mapping AON_CTRL\n"); | ||
| 398 | goto aon_err; | ||
| 399 | } | ||
| 400 | ctrl.aon_ctrl_base = base; | ||
| 401 | |||
| 402 | /* AON SRAM registers */ | ||
| 403 | base = brcmstb_ioremap_match(aon_ctrl_dt_ids, 1, NULL); | ||
| 404 | if (IS_ERR(base)) { | ||
| 405 | pr_err("error mapping AON_SRAM\n"); | ||
| 406 | goto sram_err; | ||
| 407 | } | ||
| 408 | ctrl.aon_sram_base = base; | ||
| 409 | |||
| 410 | ctrl.num_memc = 0; | ||
| 411 | /* Map MEMC DDR PHY registers */ | ||
| 412 | for_each_matching_node(dn, ddr_phy_dt_ids) { | ||
| 413 | i = ctrl.num_memc; | ||
| 414 | if (i >= MAX_NUM_MEMC) { | ||
| 415 | pr_warn("Too many MEMCs (max %d)\n", MAX_NUM_MEMC); | ||
| 416 | break; | ||
| 417 | } | ||
| 418 | base = brcmstb_ioremap_node(dn, 0); | ||
| 419 | if (IS_ERR(base)) | ||
| 420 | goto ddr_err; | ||
| 421 | |||
| 422 | ctrl.memcs[i].ddr_phy_base = base; | ||
| 423 | ctrl.num_memc++; | ||
| 424 | } | ||
| 425 | |||
| 426 | /* MEMC ARB registers */ | ||
| 427 | base = brcmstb_ioremap_match(arb_dt_ids, 0, NULL); | ||
| 428 | if (IS_ERR(base)) { | ||
| 429 | pr_err("error mapping MEMC ARB\n"); | ||
| 430 | goto ddr_err; | ||
| 431 | } | ||
| 432 | ctrl.memcs[0].arb_base = base; | ||
| 433 | |||
| 434 | /* Timer registers */ | ||
| 435 | base = brcmstb_ioremap_match(timers_ids, 0, NULL); | ||
| 436 | if (IS_ERR(base)) { | ||
| 437 | pr_err("error mapping timers\n"); | ||
| 438 | goto tmr_err; | ||
| 439 | } | ||
| 440 | ctrl.timers_base = base; | ||
| 441 | |||
| 442 | /* s3 cold boot aka s5 */ | ||
| 443 | pm_power_off = brcmstb_pm_s5; | ||
| 444 | |||
| 445 | suspend_set_ops(&brcmstb_pm_ops); | ||
| 446 | |||
| 447 | return 0; | ||
| 448 | |||
| 449 | tmr_err: | ||
| 450 | iounmap(ctrl.memcs[0].arb_base); | ||
| 451 | ddr_err: | ||
| 452 | for (i = 0; i < ctrl.num_memc; i++) | ||
| 453 | iounmap(ctrl.memcs[i].ddr_phy_base); | ||
| 454 | |||
| 455 | iounmap(ctrl.aon_sram_base); | ||
| 456 | sram_err: | ||
| 457 | iounmap(ctrl.aon_ctrl_base); | ||
| 458 | aon_err: | ||
| 459 | return PTR_ERR(base); | ||
| 460 | } | ||
| 461 | arch_initcall(brcmstb_pm_init); | ||
diff --git a/drivers/soc/bcm/brcmstb/pm/pm.h b/drivers/soc/bcm/brcmstb/pm/pm.h index 142519fdb8f8..b7d35ac70e60 100644 --- a/drivers/soc/bcm/brcmstb/pm/pm.h +++ b/drivers/soc/bcm/brcmstb/pm/pm.h | |||
| @@ -70,9 +70,20 @@ | |||
| 70 | 70 | ||
| 71 | #ifndef __ASSEMBLY__ | 71 | #ifndef __ASSEMBLY__ |
| 72 | 72 | ||
| 73 | #ifndef CONFIG_MIPS | ||
| 73 | extern const unsigned long brcmstb_pm_do_s2_sz; | 74 | extern const unsigned long brcmstb_pm_do_s2_sz; |
| 74 | extern asmlinkage int brcmstb_pm_do_s2(void __iomem *aon_ctrl_base, | 75 | extern asmlinkage int brcmstb_pm_do_s2(void __iomem *aon_ctrl_base, |
| 75 | void __iomem *ddr_phy_pll_status); | 76 | void __iomem *ddr_phy_pll_status); |
| 76 | #endif | 77 | #else |
| 78 | /* s2 asm */ | ||
| 79 | extern asmlinkage int brcm_pm_do_s2(u32 *s2_params); | ||
| 80 | |||
| 81 | /* s3 asm */ | ||
| 82 | extern asmlinkage int brcm_pm_do_s3(void __iomem *aon_ctrl_base, | ||
| 83 | int dcache_linesz); | ||
| 84 | extern int s3_reentry; | ||
| 85 | #endif /* CONFIG_MIPS */ | ||
| 86 | |||
| 87 | #endif | ||
| 77 | 88 | ||
| 78 | #endif /* __BRCMSTB_PM_H__ */ | 89 | #endif /* __BRCMSTB_PM_H__ */ |
diff --git a/drivers/soc/bcm/brcmstb/pm/s2-mips.S b/drivers/soc/bcm/brcmstb/pm/s2-mips.S new file mode 100644 index 000000000000..27a14bc46043 --- /dev/null +++ b/drivers/soc/bcm/brcmstb/pm/s2-mips.S | |||
| @@ -0,0 +1,200 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2016 Broadcom Corporation | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <asm/asm.h> | ||
| 15 | #include <asm/regdef.h> | ||
| 16 | #include <asm/mipsregs.h> | ||
| 17 | #include <asm/stackframe.h> | ||
| 18 | |||
| 19 | #include "pm.h" | ||
| 20 | |||
| 21 | .text | ||
| 22 | .set noreorder | ||
| 23 | .align 5 | ||
| 24 | |||
| 25 | /* | ||
| 26 | * a0: u32 params array | ||
| 27 | */ | ||
| 28 | LEAF(brcm_pm_do_s2) | ||
| 29 | |||
| 30 | subu sp, 64 | ||
| 31 | sw ra, 0(sp) | ||
| 32 | sw s0, 4(sp) | ||
| 33 | sw s1, 8(sp) | ||
| 34 | sw s2, 12(sp) | ||
| 35 | sw s3, 16(sp) | ||
| 36 | sw s4, 20(sp) | ||
| 37 | sw s5, 24(sp) | ||
| 38 | sw s6, 28(sp) | ||
| 39 | sw s7, 32(sp) | ||
| 40 | |||
| 41 | /* | ||
| 42 | * Dereference the params array | ||
| 43 | * s0: AON_CTRL base register | ||
| 44 | * s1: DDR_PHY base register | ||
| 45 | * s2: TIMERS base register | ||
| 46 | * s3: I-Cache line size | ||
| 47 | * s4: Restart vector address | ||
| 48 | * s5: Restart vector size | ||
| 49 | */ | ||
| 50 | move t0, a0 | ||
| 51 | |||
| 52 | lw s0, 0(t0) | ||
| 53 | lw s1, 4(t0) | ||
| 54 | lw s2, 8(t0) | ||
| 55 | lw s3, 12(t0) | ||
| 56 | lw s4, 16(t0) | ||
| 57 | lw s5, 20(t0) | ||
| 58 | |||
| 59 | /* Lock this asm section into the I-cache */ | ||
| 60 | addiu t1, s3, -1 | ||
| 61 | not t1 | ||
| 62 | |||
| 63 | la t0, brcm_pm_do_s2 | ||
| 64 | and t0, t1 | ||
| 65 | |||
| 66 | la t2, asm_end | ||
| 67 | and t2, t1 | ||
| 68 | |||
| 69 | 1: cache 0x1c, 0(t0) | ||
| 70 | bne t0, t2, 1b | ||
| 71 | addu t0, s3 | ||
| 72 | |||
| 73 | /* Lock the interrupt vector into the I-cache */ | ||
| 74 | move t0, zero | ||
| 75 | |||
| 76 | 2: move t1, s4 | ||
| 77 | cache 0x1c, 0(t1) | ||
| 78 | addu t1, s3 | ||
| 79 | addu t0, s3 | ||
| 80 | ble t0, s5, 2b | ||
| 81 | nop | ||
| 82 | |||
| 83 | sync | ||
| 84 | |||
| 85 | /* Power down request */ | ||
| 86 | li t0, PM_S2_COMMAND | ||
| 87 | sw zero, AON_CTRL_PM_CTRL(s0) | ||
| 88 | lw zero, AON_CTRL_PM_CTRL(s0) | ||
| 89 | sw t0, AON_CTRL_PM_CTRL(s0) | ||
| 90 | lw t0, AON_CTRL_PM_CTRL(s0) | ||
| 91 | |||
| 92 | /* Enable CP0 interrupt 2 and wait for interrupt */ | ||
| 93 | mfc0 t0, CP0_STATUS | ||
| 94 | /* Save cp0 sr for restoring later */ | ||
| 95 | move s6, t0 | ||
| 96 | |||
| 97 | li t1, ~(ST0_IM | ST0_IE) | ||
| 98 | and t0, t1 | ||
| 99 | ori t0, STATUSF_IP2 | ||
| 100 | mtc0 t0, CP0_STATUS | ||
| 101 | nop | ||
| 102 | nop | ||
| 103 | nop | ||
| 104 | ori t0, ST0_IE | ||
| 105 | mtc0 t0, CP0_STATUS | ||
| 106 | |||
| 107 | /* Wait for interrupt */ | ||
| 108 | wait | ||
| 109 | nop | ||
| 110 | |||
| 111 | /* Wait for memc0 */ | ||
| 112 | 1: lw t0, DDR40_PHY_CONTROL_REGS_0_PLL_STATUS(s1) | ||
| 113 | andi t0, 1 | ||
| 114 | beqz t0, 1b | ||
| 115 | nop | ||
| 116 | |||
| 117 | /* 1ms delay needed for stable recovery */ | ||
| 118 | /* Use TIMER1 to count 1 ms */ | ||
| 119 | li t0, RESET_TIMER | ||
| 120 | sw t0, TIMER_TIMER1_CTRL(s2) | ||
| 121 | lw t0, TIMER_TIMER1_CTRL(s2) | ||
| 122 | |||
| 123 | li t0, START_TIMER | ||
| 124 | sw t0, TIMER_TIMER1_CTRL(s2) | ||
| 125 | lw t0, TIMER_TIMER1_CTRL(s2) | ||
| 126 | |||
| 127 | /* Prepare delay */ | ||
| 128 | li t0, TIMER_MASK | ||
| 129 | lw t1, TIMER_TIMER1_STAT(s2) | ||
| 130 | and t1, t0 | ||
| 131 | /* 1ms delay */ | ||
| 132 | addi t1, 27000 | ||
| 133 | |||
| 134 | /* Wait for the timer value to exceed t1 */ | ||
| 135 | 1: lw t0, TIMER_TIMER1_STAT(s2) | ||
| 136 | sgtu t2, t1, t0 | ||
| 137 | bnez t2, 1b | ||
| 138 | nop | ||
| 139 | |||
| 140 | /* Power back up */ | ||
| 141 | li t1, 1 | ||
| 142 | sw t1, AON_CTRL_HOST_MISC_CMDS(s0) | ||
| 143 | lw t1, AON_CTRL_HOST_MISC_CMDS(s0) | ||
| 144 | |||
| 145 | sw zero, AON_CTRL_PM_CTRL(s0) | ||
| 146 | lw zero, AON_CTRL_PM_CTRL(s0) | ||
| 147 | |||
| 148 | /* Unlock I-cache */ | ||
| 149 | addiu t1, s3, -1 | ||
| 150 | not t1 | ||
| 151 | |||
| 152 | la t0, brcm_pm_do_s2 | ||
| 153 | and t0, t1 | ||
| 154 | |||
| 155 | la t2, asm_end | ||
| 156 | and t2, t1 | ||
| 157 | |||
| 158 | 1: cache 0x00, 0(t0) | ||
| 159 | bne t0, t2, 1b | ||
| 160 | addu t0, s3 | ||
| 161 | |||
| 162 | /* Unlock interrupt vector */ | ||
| 163 | move t0, zero | ||
| 164 | |||
| 165 | 2: move t1, s4 | ||
| 166 | cache 0x00, 0(t1) | ||
| 167 | addu t1, s3 | ||
| 168 | addu t0, s3 | ||
| 169 | ble t0, s5, 2b | ||
| 170 | nop | ||
| 171 | |||
| 172 | /* Restore cp0 sr */ | ||
| 173 | sync | ||
| 174 | nop | ||
| 175 | mtc0 s6, CP0_STATUS | ||
| 176 | nop | ||
| 177 | |||
| 178 | /* Set return value to success */ | ||
| 179 | li v0, 0 | ||
| 180 | |||
| 181 | /* Return to caller */ | ||
| 182 | lw s7, 32(sp) | ||
| 183 | lw s6, 28(sp) | ||
| 184 | lw s5, 24(sp) | ||
| 185 | lw s4, 20(sp) | ||
| 186 | lw s3, 16(sp) | ||
| 187 | lw s2, 12(sp) | ||
| 188 | lw s1, 8(sp) | ||
| 189 | lw s0, 4(sp) | ||
| 190 | lw ra, 0(sp) | ||
| 191 | addiu sp, 64 | ||
| 192 | |||
| 193 | jr ra | ||
| 194 | nop | ||
| 195 | END(brcm_pm_do_s2) | ||
| 196 | |||
| 197 | .globl asm_end | ||
| 198 | asm_end: | ||
| 199 | nop | ||
| 200 | |||
diff --git a/drivers/soc/bcm/brcmstb/pm/s3-mips.S b/drivers/soc/bcm/brcmstb/pm/s3-mips.S new file mode 100644 index 000000000000..1242308a8868 --- /dev/null +++ b/drivers/soc/bcm/brcmstb/pm/s3-mips.S | |||
| @@ -0,0 +1,146 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2016 Broadcom Corporation | ||
| 3 | * | ||
| 4 | * This program is free software; you can redistribute it and/or modify | ||
| 5 | * it under the terms of the GNU General Public License version 2 as | ||
| 6 | * published by the Free Software Foundation. | ||
| 7 | * | ||
| 8 | * This program is distributed in the hope that it will be useful, | ||
| 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 11 | * GNU General Public License for more details. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <asm/asm.h> | ||
| 15 | #include <asm/regdef.h> | ||
| 16 | #include <asm/mipsregs.h> | ||
| 17 | #include <asm/bmips.h> | ||
| 18 | |||
| 19 | #include "pm.h" | ||
| 20 | |||
| 21 | .text | ||
| 22 | .set noreorder | ||
| 23 | .align 5 | ||
| 24 | .global s3_reentry | ||
| 25 | |||
| 26 | /* | ||
| 27 | * a0: AON_CTRL base register | ||
| 28 | * a1: D-Cache line size | ||
| 29 | */ | ||
| 30 | LEAF(brcm_pm_do_s3) | ||
| 31 | |||
| 32 | /* Get the address of s3_context */ | ||
| 33 | la t0, gp_regs | ||
| 34 | sw ra, 0(t0) | ||
| 35 | sw s0, 4(t0) | ||
| 36 | sw s1, 8(t0) | ||
| 37 | sw s2, 12(t0) | ||
| 38 | sw s3, 16(t0) | ||
| 39 | sw s4, 20(t0) | ||
| 40 | sw s5, 24(t0) | ||
| 41 | sw s6, 28(t0) | ||
| 42 | sw s7, 32(t0) | ||
| 43 | sw gp, 36(t0) | ||
| 44 | sw sp, 40(t0) | ||
| 45 | sw fp, 44(t0) | ||
| 46 | |||
| 47 | /* Save CP0 Status */ | ||
| 48 | mfc0 t1, CP0_STATUS | ||
| 49 | sw t1, 48(t0) | ||
| 50 | |||
| 51 | /* Write-back gp registers - cache will be gone */ | ||
| 52 | addiu t1, a1, -1 | ||
| 53 | not t1 | ||
| 54 | and t0, t1 | ||
| 55 | |||
| 56 | /* Flush at least 64 bytes */ | ||
| 57 | addiu t2, t0, 64 | ||
| 58 | and t2, t1 | ||
| 59 | |||
| 60 | 1: cache 0x17, 0(t0) | ||
| 61 | bne t0, t2, 1b | ||
| 62 | addu t0, a1 | ||
| 63 | |||
| 64 | /* Drop to deep standby */ | ||
| 65 | li t1, PM_WARM_CONFIG | ||
| 66 | sw zero, AON_CTRL_PM_CTRL(a0) | ||
| 67 | lw zero, AON_CTRL_PM_CTRL(a0) | ||
| 68 | sw t1, AON_CTRL_PM_CTRL(a0) | ||
| 69 | lw t1, AON_CTRL_PM_CTRL(a0) | ||
| 70 | |||
| 71 | li t1, (PM_WARM_CONFIG | PM_PWR_DOWN) | ||
| 72 | sw t1, AON_CTRL_PM_CTRL(a0) | ||
| 73 | lw t1, AON_CTRL_PM_CTRL(a0) | ||
| 74 | |||
| 75 | /* Enable CP0 interrupt 2 and wait for interrupt */ | ||
| 76 | mfc0 t0, CP0_STATUS | ||
| 77 | |||
| 78 | li t1, ~(ST0_IM | ST0_IE) | ||
| 79 | and t0, t1 | ||
| 80 | ori t0, STATUSF_IP2 | ||
| 81 | mtc0 t0, CP0_STATUS | ||
| 82 | nop | ||
| 83 | nop | ||
| 84 | nop | ||
| 85 | ori t0, ST0_IE | ||
| 86 | mtc0 t0, CP0_STATUS | ||
| 87 | |||
| 88 | /* Wait for interrupt */ | ||
| 89 | wait | ||
| 90 | nop | ||
| 91 | |||
| 92 | s3_reentry: | ||
| 93 | |||
| 94 | /* Clear call/return stack */ | ||
| 95 | li t0, (0x06 << 16) | ||
| 96 | mtc0 t0, $22, 2 | ||
| 97 | ssnop | ||
| 98 | ssnop | ||
| 99 | ssnop | ||
| 100 | |||
| 101 | /* Clear jump target buffer */ | ||
| 102 | li t0, (0x04 << 16) | ||
| 103 | mtc0 t0, $22, 2 | ||
| 104 | ssnop | ||
| 105 | ssnop | ||
| 106 | ssnop | ||
| 107 | |||
| 108 | sync | ||
| 109 | nop | ||
| 110 | |||
| 111 | /* Setup mmu defaults */ | ||
| 112 | mtc0 zero, CP0_WIRED | ||
| 113 | mtc0 zero, CP0_ENTRYHI | ||
| 114 | li k0, PM_DEFAULT_MASK | ||
| 115 | mtc0 k0, CP0_PAGEMASK | ||
| 116 | |||
| 117 | li sp, BMIPS_WARM_RESTART_VEC | ||
| 118 | la k0, plat_wired_tlb_setup | ||
| 119 | jalr k0 | ||
| 120 | nop | ||
| 121 | |||
| 122 | /* Restore general purpose registers */ | ||
| 123 | la t0, gp_regs | ||
| 124 | lw fp, 44(t0) | ||
| 125 | lw sp, 40(t0) | ||
| 126 | lw gp, 36(t0) | ||
| 127 | lw s7, 32(t0) | ||
| 128 | lw s6, 28(t0) | ||
| 129 | lw s5, 24(t0) | ||
| 130 | lw s4, 20(t0) | ||
| 131 | lw s3, 16(t0) | ||
| 132 | lw s2, 12(t0) | ||
| 133 | lw s1, 8(t0) | ||
| 134 | lw s0, 4(t0) | ||
| 135 | lw ra, 0(t0) | ||
| 136 | |||
| 137 | /* Restore CP0 status */ | ||
| 138 | lw t1, 48(t0) | ||
| 139 | mtc0 t1, CP0_STATUS | ||
| 140 | |||
| 141 | /* Return to caller */ | ||
| 142 | li v0, 0 | ||
| 143 | jr ra | ||
| 144 | nop | ||
| 145 | |||
| 146 | END(brcm_pm_do_s3) | ||
