xref: /openbmc/linux/drivers/soc/bcm/brcmstb/pm/pm-mips.c (revision f5029f62)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20e9b1141SJustin Chen /*
30e9b1141SJustin Chen  * MIPS-specific support for Broadcom STB S2/S3/S5 power management
40e9b1141SJustin Chen  *
50e9b1141SJustin Chen  * Copyright (C) 2016-2017 Broadcom
60e9b1141SJustin Chen  */
70e9b1141SJustin Chen 
80e9b1141SJustin Chen #include <linux/kernel.h>
90e9b1141SJustin Chen #include <linux/printk.h>
100e9b1141SJustin Chen #include <linux/io.h>
110e9b1141SJustin Chen #include <linux/of.h>
120e9b1141SJustin Chen #include <linux/of_address.h>
130e9b1141SJustin Chen #include <linux/delay.h>
140e9b1141SJustin Chen #include <linux/suspend.h>
150e9b1141SJustin Chen #include <asm/bmips.h>
160e9b1141SJustin Chen #include <asm/tlbflush.h>
170e9b1141SJustin Chen 
180e9b1141SJustin Chen #include "pm.h"
190e9b1141SJustin Chen 
200e9b1141SJustin Chen #define S2_NUM_PARAMS		6
210e9b1141SJustin Chen #define MAX_NUM_MEMC		3
220e9b1141SJustin Chen 
230e9b1141SJustin Chen /* S3 constants */
240e9b1141SJustin Chen #define MAX_GP_REGS		16
250e9b1141SJustin Chen #define MAX_CP0_REGS		32
260e9b1141SJustin Chen #define NUM_MEMC_CLIENTS	128
270e9b1141SJustin Chen #define AON_CTRL_RAM_SIZE	128
280e9b1141SJustin Chen #define BRCMSTB_S3_MAGIC	0x5AFEB007
290e9b1141SJustin Chen 
300e9b1141SJustin Chen #define CLEAR_RESET_MASK	0x01
310e9b1141SJustin Chen 
320e9b1141SJustin Chen /* Index each CP0 register that needs to be saved */
330e9b1141SJustin Chen #define CONTEXT		0
340e9b1141SJustin Chen #define USER_LOCAL	1
350e9b1141SJustin Chen #define PGMK		2
360e9b1141SJustin Chen #define HWRENA		3
370e9b1141SJustin Chen #define COMPARE		4
380e9b1141SJustin Chen #define STATUS		5
390e9b1141SJustin Chen #define CONFIG		6
400e9b1141SJustin Chen #define MODE		7
410e9b1141SJustin Chen #define EDSP		8
420e9b1141SJustin Chen #define BOOT_VEC	9
430e9b1141SJustin Chen #define EBASE		10
440e9b1141SJustin Chen 
450e9b1141SJustin Chen struct brcmstb_memc {
460e9b1141SJustin Chen 	void __iomem *ddr_phy_base;
470e9b1141SJustin Chen 	void __iomem *arb_base;
480e9b1141SJustin Chen };
490e9b1141SJustin Chen 
500e9b1141SJustin Chen struct brcmstb_pm_control {
510e9b1141SJustin Chen 	void __iomem *aon_ctrl_base;
520e9b1141SJustin Chen 	void __iomem *aon_sram_base;
530e9b1141SJustin Chen 	void __iomem *timers_base;
540e9b1141SJustin Chen 	struct brcmstb_memc memcs[MAX_NUM_MEMC];
550e9b1141SJustin Chen 	int num_memc;
560e9b1141SJustin Chen };
570e9b1141SJustin Chen 
580e9b1141SJustin Chen struct brcm_pm_s3_context {
590e9b1141SJustin Chen 	u32			cp0_regs[MAX_CP0_REGS];
600e9b1141SJustin Chen 	u32			memc0_rts[NUM_MEMC_CLIENTS];
610e9b1141SJustin Chen 	u32			sc_boot_vec;
620e9b1141SJustin Chen };
630e9b1141SJustin Chen 
640e9b1141SJustin Chen struct brcmstb_mem_transfer;
650e9b1141SJustin Chen 
660e9b1141SJustin Chen struct brcmstb_mem_transfer {
670e9b1141SJustin Chen 	struct brcmstb_mem_transfer	*next;
680e9b1141SJustin Chen 	void				*src;
690e9b1141SJustin Chen 	void				*dst;
700e9b1141SJustin Chen 	dma_addr_t			pa_src;
710e9b1141SJustin Chen 	dma_addr_t			pa_dst;
720e9b1141SJustin Chen 	u32				len;
730e9b1141SJustin Chen 	u8				key;
740e9b1141SJustin Chen 	u8				mode;
750e9b1141SJustin Chen 	u8				src_remapped;
760e9b1141SJustin Chen 	u8				dst_remapped;
770e9b1141SJustin Chen 	u8				src_dst_remapped;
780e9b1141SJustin Chen };
790e9b1141SJustin Chen 
800e9b1141SJustin Chen #define AON_SAVE_SRAM(base, idx, val) \
810e9b1141SJustin Chen 	__raw_writel(val, base + (idx << 2))
820e9b1141SJustin Chen 
830e9b1141SJustin Chen /* Used for saving registers in asm */
840e9b1141SJustin Chen u32 gp_regs[MAX_GP_REGS];
850e9b1141SJustin Chen 
860e9b1141SJustin Chen #define	BSP_CLOCK_STOP		0x00
870e9b1141SJustin Chen #define PM_INITIATE		0x01
880e9b1141SJustin Chen 
890e9b1141SJustin Chen static struct brcmstb_pm_control ctrl;
900e9b1141SJustin Chen 
brcm_pm_save_cp0_context(struct brcm_pm_s3_context * ctx)910e9b1141SJustin Chen static void brcm_pm_save_cp0_context(struct brcm_pm_s3_context *ctx)
920e9b1141SJustin Chen {
930e9b1141SJustin Chen 	/* Generic MIPS */
940e9b1141SJustin Chen 	ctx->cp0_regs[CONTEXT] = read_c0_context();
950e9b1141SJustin Chen 	ctx->cp0_regs[USER_LOCAL] = read_c0_userlocal();
960e9b1141SJustin Chen 	ctx->cp0_regs[PGMK] = read_c0_pagemask();
970e9b1141SJustin Chen 	ctx->cp0_regs[HWRENA] = read_c0_cache();
980e9b1141SJustin Chen 	ctx->cp0_regs[COMPARE] = read_c0_compare();
990e9b1141SJustin Chen 	ctx->cp0_regs[STATUS] = read_c0_status();
1000e9b1141SJustin Chen 
1010e9b1141SJustin Chen 	/* Broadcom specific */
1020e9b1141SJustin Chen 	ctx->cp0_regs[CONFIG] = read_c0_brcm_config();
1030e9b1141SJustin Chen 	ctx->cp0_regs[MODE] = read_c0_brcm_mode();
1040e9b1141SJustin Chen 	ctx->cp0_regs[EDSP] = read_c0_brcm_edsp();
1050e9b1141SJustin Chen 	ctx->cp0_regs[BOOT_VEC] = read_c0_brcm_bootvec();
1060e9b1141SJustin Chen 	ctx->cp0_regs[EBASE] = read_c0_ebase();
1070e9b1141SJustin Chen 
1080e9b1141SJustin Chen 	ctx->sc_boot_vec = bmips_read_zscm_reg(0xa0);
1090e9b1141SJustin Chen }
1100e9b1141SJustin Chen 
brcm_pm_restore_cp0_context(struct brcm_pm_s3_context * ctx)1110e9b1141SJustin Chen static void brcm_pm_restore_cp0_context(struct brcm_pm_s3_context *ctx)
1120e9b1141SJustin Chen {
1130e9b1141SJustin Chen 	/* Restore cp0 state */
1140e9b1141SJustin Chen 	bmips_write_zscm_reg(0xa0, ctx->sc_boot_vec);
1150e9b1141SJustin Chen 
1160e9b1141SJustin Chen 	/* Generic MIPS */
1170e9b1141SJustin Chen 	write_c0_context(ctx->cp0_regs[CONTEXT]);
1180e9b1141SJustin Chen 	write_c0_userlocal(ctx->cp0_regs[USER_LOCAL]);
1190e9b1141SJustin Chen 	write_c0_pagemask(ctx->cp0_regs[PGMK]);
1200e9b1141SJustin Chen 	write_c0_cache(ctx->cp0_regs[HWRENA]);
1210e9b1141SJustin Chen 	write_c0_compare(ctx->cp0_regs[COMPARE]);
1220e9b1141SJustin Chen 	write_c0_status(ctx->cp0_regs[STATUS]);
1230e9b1141SJustin Chen 
1240e9b1141SJustin Chen 	/* Broadcom specific */
1250e9b1141SJustin Chen 	write_c0_brcm_config(ctx->cp0_regs[CONFIG]);
1260e9b1141SJustin Chen 	write_c0_brcm_mode(ctx->cp0_regs[MODE]);
1270e9b1141SJustin Chen 	write_c0_brcm_edsp(ctx->cp0_regs[EDSP]);
1280e9b1141SJustin Chen 	write_c0_brcm_bootvec(ctx->cp0_regs[BOOT_VEC]);
1290e9b1141SJustin Chen 	write_c0_ebase(ctx->cp0_regs[EBASE]);
1300e9b1141SJustin Chen }
1310e9b1141SJustin Chen 
brcmstb_pm_handshake(void)1320e9b1141SJustin Chen static void  brcmstb_pm_handshake(void)
1330e9b1141SJustin Chen {
1340e9b1141SJustin Chen 	void __iomem *base = ctrl.aon_ctrl_base;
1350e9b1141SJustin Chen 	u32 tmp;
1360e9b1141SJustin Chen 
1370e9b1141SJustin Chen 	/* BSP power handshake, v1 */
1380e9b1141SJustin Chen 	tmp = __raw_readl(base + AON_CTRL_HOST_MISC_CMDS);
1390e9b1141SJustin Chen 	tmp &= ~1UL;
1400e9b1141SJustin Chen 	__raw_writel(tmp, base + AON_CTRL_HOST_MISC_CMDS);
1410e9b1141SJustin Chen 	(void)__raw_readl(base + AON_CTRL_HOST_MISC_CMDS);
1420e9b1141SJustin Chen 
1430e9b1141SJustin Chen 	__raw_writel(0, base + AON_CTRL_PM_INITIATE);
1440e9b1141SJustin Chen 	(void)__raw_readl(base + AON_CTRL_PM_INITIATE);
1450e9b1141SJustin Chen 	__raw_writel(BSP_CLOCK_STOP | PM_INITIATE,
1460e9b1141SJustin Chen 		     base + AON_CTRL_PM_INITIATE);
1470e9b1141SJustin Chen 	/*
1480e9b1141SJustin Chen 	 * HACK: BSP may have internal race on the CLOCK_STOP command.
1490e9b1141SJustin Chen 	 * Avoid touching the BSP for a few milliseconds.
1500e9b1141SJustin Chen 	 */
1510e9b1141SJustin Chen 	mdelay(3);
1520e9b1141SJustin Chen }
1530e9b1141SJustin Chen 
brcmstb_pm_s5(void)1540e9b1141SJustin Chen static void brcmstb_pm_s5(void)
1550e9b1141SJustin Chen {
1560e9b1141SJustin Chen 	void __iomem *base = ctrl.aon_ctrl_base;
1570e9b1141SJustin Chen 
1580e9b1141SJustin Chen 	brcmstb_pm_handshake();
1590e9b1141SJustin Chen 
1600e9b1141SJustin Chen 	/* Clear magic s3 warm-boot value */
1610e9b1141SJustin Chen 	AON_SAVE_SRAM(ctrl.aon_sram_base, 0, 0);
1620e9b1141SJustin Chen 
1630e9b1141SJustin Chen 	/* Set the countdown */
1640e9b1141SJustin Chen 	__raw_writel(0x10, base + AON_CTRL_PM_CPU_WAIT_COUNT);
1650e9b1141SJustin Chen 	(void)__raw_readl(base + AON_CTRL_PM_CPU_WAIT_COUNT);
1660e9b1141SJustin Chen 
1670e9b1141SJustin Chen 	/* Prepare to S5 cold boot */
1680e9b1141SJustin Chen 	__raw_writel(PM_COLD_CONFIG, base + AON_CTRL_PM_CTRL);
1690e9b1141SJustin Chen 	(void)__raw_readl(base + AON_CTRL_PM_CTRL);
1700e9b1141SJustin Chen 
1710e9b1141SJustin Chen 	__raw_writel((PM_COLD_CONFIG | PM_PWR_DOWN), base +
1720e9b1141SJustin Chen 		      AON_CTRL_PM_CTRL);
1730e9b1141SJustin Chen 	(void)__raw_readl(base + AON_CTRL_PM_CTRL);
1740e9b1141SJustin Chen 
1750e9b1141SJustin Chen 	__asm__ __volatile__(
1760e9b1141SJustin Chen 	"	wait\n"
1770e9b1141SJustin Chen 	: : : "memory");
1780e9b1141SJustin Chen }
1790e9b1141SJustin Chen 
brcmstb_pm_s3(void)1800e9b1141SJustin Chen static int brcmstb_pm_s3(void)
1810e9b1141SJustin Chen {
1820e9b1141SJustin Chen 	struct brcm_pm_s3_context s3_context;
1830e9b1141SJustin Chen 	void __iomem *memc_arb_base;
1840e9b1141SJustin Chen 	unsigned long flags;
1850e9b1141SJustin Chen 	u32 tmp;
1860e9b1141SJustin Chen 	int i;
1870e9b1141SJustin Chen 
1880e9b1141SJustin Chen 	/* Prepare for s3 */
1890e9b1141SJustin Chen 	AON_SAVE_SRAM(ctrl.aon_sram_base, 0, BRCMSTB_S3_MAGIC);
1900e9b1141SJustin Chen 	AON_SAVE_SRAM(ctrl.aon_sram_base, 1, (u32)&s3_reentry);
1910e9b1141SJustin Chen 	AON_SAVE_SRAM(ctrl.aon_sram_base, 2, 0);
1920e9b1141SJustin Chen 
1930e9b1141SJustin Chen 	/* Clear RESET_HISTORY */
1940e9b1141SJustin Chen 	tmp = __raw_readl(ctrl.aon_ctrl_base + AON_CTRL_RESET_CTRL);
1950e9b1141SJustin Chen 	tmp &= ~CLEAR_RESET_MASK;
1960e9b1141SJustin Chen 	__raw_writel(tmp, ctrl.aon_ctrl_base + AON_CTRL_RESET_CTRL);
1970e9b1141SJustin Chen 
1980e9b1141SJustin Chen 	local_irq_save(flags);
1990e9b1141SJustin Chen 
2000e9b1141SJustin Chen 	/* Inhibit DDR_RSTb pulse for both MMCs*/
2010e9b1141SJustin Chen 	for (i = 0; i < ctrl.num_memc; i++) {
2020e9b1141SJustin Chen 		tmp = __raw_readl(ctrl.memcs[i].ddr_phy_base +
2030e9b1141SJustin Chen 			DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL);
2040e9b1141SJustin Chen 
2050e9b1141SJustin Chen 		tmp &= ~0x0f;
2060e9b1141SJustin Chen 		__raw_writel(tmp, ctrl.memcs[i].ddr_phy_base +
2070e9b1141SJustin Chen 			DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL);
2080e9b1141SJustin Chen 		tmp |= (0x05 | BIT(5));
2090e9b1141SJustin Chen 		__raw_writel(tmp, ctrl.memcs[i].ddr_phy_base +
2100e9b1141SJustin Chen 			DDR40_PHY_CONTROL_REGS_0_STANDBY_CTRL);
2110e9b1141SJustin Chen 	}
2120e9b1141SJustin Chen 
2130e9b1141SJustin Chen 	/* Save CP0 context */
2140e9b1141SJustin Chen 	brcm_pm_save_cp0_context(&s3_context);
2150e9b1141SJustin Chen 
2160e9b1141SJustin Chen 	/* Save RTS(skip debug register) */
2170e9b1141SJustin Chen 	memc_arb_base = ctrl.memcs[0].arb_base + 4;
2180e9b1141SJustin Chen 	for (i = 0; i < NUM_MEMC_CLIENTS; i++) {
2190e9b1141SJustin Chen 		s3_context.memc0_rts[i] = __raw_readl(memc_arb_base);
2200e9b1141SJustin Chen 		memc_arb_base += 4;
2210e9b1141SJustin Chen 	}
2220e9b1141SJustin Chen 
2230e9b1141SJustin Chen 	/* Save I/O context */
2240e9b1141SJustin Chen 	local_flush_tlb_all();
2250e9b1141SJustin Chen 	_dma_cache_wback_inv(0, ~0);
2260e9b1141SJustin Chen 
2270e9b1141SJustin Chen 	brcm_pm_do_s3(ctrl.aon_ctrl_base, current_cpu_data.dcache.linesz);
2280e9b1141SJustin Chen 
2290e9b1141SJustin Chen 	/* CPU reconfiguration */
2300e9b1141SJustin Chen 	local_flush_tlb_all();
2310e9b1141SJustin Chen 	bmips_cpu_setup();
2320e9b1141SJustin Chen 	cpumask_clear(&bmips_booted_mask);
2330e9b1141SJustin Chen 
2340e9b1141SJustin Chen 	/* Restore RTS (skip debug register) */
2350e9b1141SJustin Chen 	memc_arb_base = ctrl.memcs[0].arb_base + 4;
2360e9b1141SJustin Chen 	for (i = 0; i < NUM_MEMC_CLIENTS; i++) {
2370e9b1141SJustin Chen 		__raw_writel(s3_context.memc0_rts[i], memc_arb_base);
2380e9b1141SJustin Chen 		memc_arb_base += 4;
2390e9b1141SJustin Chen 	}
2400e9b1141SJustin Chen 
2410e9b1141SJustin Chen 	/* restore CP0 context */
2420e9b1141SJustin Chen 	brcm_pm_restore_cp0_context(&s3_context);
2430e9b1141SJustin Chen 
2440e9b1141SJustin Chen 	local_irq_restore(flags);
2450e9b1141SJustin Chen 
2460e9b1141SJustin Chen 	return 0;
2470e9b1141SJustin Chen }
2480e9b1141SJustin Chen 
brcmstb_pm_s2(void)2490e9b1141SJustin Chen static int brcmstb_pm_s2(void)
2500e9b1141SJustin Chen {
2510e9b1141SJustin Chen 	/*
2520e9b1141SJustin Chen 	 * We need to pass 6 arguments to an assembly function. Lets avoid the
2530e9b1141SJustin Chen 	 * stack and pass arguments in a explicit 4 byte array. The assembly
2540e9b1141SJustin Chen 	 * code assumes all arguments are 4 bytes and arguments are ordered
2550e9b1141SJustin Chen 	 * like so:
2560e9b1141SJustin Chen 	 *
2570e9b1141SJustin Chen 	 * 0: AON_CTRl base register
2580e9b1141SJustin Chen 	 * 1: DDR_PHY base register
2590e9b1141SJustin Chen 	 * 2: TIMERS base resgister
2600e9b1141SJustin Chen 	 * 3: I-Cache line size
2610e9b1141SJustin Chen 	 * 4: Restart vector address
2620e9b1141SJustin Chen 	 * 5: Restart vector size
2630e9b1141SJustin Chen 	 */
2640e9b1141SJustin Chen 	u32 s2_params[6];
2650e9b1141SJustin Chen 
2660e9b1141SJustin Chen 	/* Prepare s2 parameters */
2670e9b1141SJustin Chen 	s2_params[0] = (u32)ctrl.aon_ctrl_base;
2680e9b1141SJustin Chen 	s2_params[1] = (u32)ctrl.memcs[0].ddr_phy_base;
2690e9b1141SJustin Chen 	s2_params[2] = (u32)ctrl.timers_base;
2700e9b1141SJustin Chen 	s2_params[3] = (u32)current_cpu_data.icache.linesz;
2710e9b1141SJustin Chen 	s2_params[4] = (u32)BMIPS_WARM_RESTART_VEC;
2720e9b1141SJustin Chen 	s2_params[5] = (u32)(bmips_smp_int_vec_end -
2730e9b1141SJustin Chen 		bmips_smp_int_vec);
2740e9b1141SJustin Chen 
2750e9b1141SJustin Chen 	/* Drop to standby */
2760e9b1141SJustin Chen 	brcm_pm_do_s2(s2_params);
2770e9b1141SJustin Chen 
2780e9b1141SJustin Chen 	return 0;
2790e9b1141SJustin Chen }
2800e9b1141SJustin Chen 
brcmstb_pm_standby(bool deep_standby)2810e9b1141SJustin Chen static int brcmstb_pm_standby(bool deep_standby)
2820e9b1141SJustin Chen {
2830e9b1141SJustin Chen 	brcmstb_pm_handshake();
2840e9b1141SJustin Chen 
2850e9b1141SJustin Chen 	/* Send IRQs to BMIPS_WARM_RESTART_VEC */
2860e9b1141SJustin Chen 	clear_c0_cause(CAUSEF_IV);
2870e9b1141SJustin Chen 	irq_disable_hazard();
2880e9b1141SJustin Chen 	set_c0_status(ST0_BEV);
2890e9b1141SJustin Chen 	irq_disable_hazard();
2900e9b1141SJustin Chen 
2910e9b1141SJustin Chen 	if (deep_standby)
2920e9b1141SJustin Chen 		brcmstb_pm_s3();
2930e9b1141SJustin Chen 	else
2940e9b1141SJustin Chen 		brcmstb_pm_s2();
2950e9b1141SJustin Chen 
2960e9b1141SJustin Chen 	/* Send IRQs to normal runtime vectors */
2970e9b1141SJustin Chen 	clear_c0_status(ST0_BEV);
2980e9b1141SJustin Chen 	irq_disable_hazard();
2990e9b1141SJustin Chen 	set_c0_cause(CAUSEF_IV);
3000e9b1141SJustin Chen 	irq_disable_hazard();
3010e9b1141SJustin Chen 
3020e9b1141SJustin Chen 	return 0;
3030e9b1141SJustin Chen }
3040e9b1141SJustin Chen 
brcmstb_pm_enter(suspend_state_t state)3050e9b1141SJustin Chen static int brcmstb_pm_enter(suspend_state_t state)
3060e9b1141SJustin Chen {
3070e9b1141SJustin Chen 	int ret = -EINVAL;
3080e9b1141SJustin Chen 
3090e9b1141SJustin Chen 	switch (state) {
3100e9b1141SJustin Chen 	case PM_SUSPEND_STANDBY:
3110e9b1141SJustin Chen 		ret = brcmstb_pm_standby(false);
3120e9b1141SJustin Chen 		break;
3130e9b1141SJustin Chen 	case PM_SUSPEND_MEM:
3140e9b1141SJustin Chen 		ret = brcmstb_pm_standby(true);
3150e9b1141SJustin Chen 		break;
3160e9b1141SJustin Chen 	}
3170e9b1141SJustin Chen 
3180e9b1141SJustin Chen 	return ret;
3190e9b1141SJustin Chen }
3200e9b1141SJustin Chen 
brcmstb_pm_valid(suspend_state_t state)3210e9b1141SJustin Chen static int brcmstb_pm_valid(suspend_state_t state)
3220e9b1141SJustin Chen {
3230e9b1141SJustin Chen 	switch (state) {
3240e9b1141SJustin Chen 	case PM_SUSPEND_STANDBY:
3250e9b1141SJustin Chen 		return true;
3260e9b1141SJustin Chen 	case PM_SUSPEND_MEM:
3270e9b1141SJustin Chen 		return true;
3280e9b1141SJustin Chen 	default:
3290e9b1141SJustin Chen 		return false;
3300e9b1141SJustin Chen 	}
3310e9b1141SJustin Chen }
3320e9b1141SJustin Chen 
3330e9b1141SJustin Chen static const struct platform_suspend_ops brcmstb_pm_ops = {
3340e9b1141SJustin Chen 	.enter		= brcmstb_pm_enter,
3350e9b1141SJustin Chen 	.valid		= brcmstb_pm_valid,
3360e9b1141SJustin Chen };
3370e9b1141SJustin Chen 
3380e9b1141SJustin Chen static const struct of_device_id aon_ctrl_dt_ids[] = {
3390e9b1141SJustin Chen 	{ .compatible = "brcm,brcmstb-aon-ctrl" },
3400e9b1141SJustin Chen 	{ /* sentinel */ }
3410e9b1141SJustin Chen };
3420e9b1141SJustin Chen 
3430e9b1141SJustin Chen static const struct of_device_id ddr_phy_dt_ids[] = {
3440e9b1141SJustin Chen 	{ .compatible = "brcm,brcmstb-ddr-phy" },
3450e9b1141SJustin Chen 	{ /* sentinel */ }
3460e9b1141SJustin Chen };
3470e9b1141SJustin Chen 
3480e9b1141SJustin Chen static const struct of_device_id arb_dt_ids[] = {
3490e9b1141SJustin Chen 	{ .compatible = "brcm,brcmstb-memc-arb" },
3500e9b1141SJustin Chen 	{ /* sentinel */ }
3510e9b1141SJustin Chen };
3520e9b1141SJustin Chen 
3530e9b1141SJustin Chen static const struct of_device_id timers_ids[] = {
3540e9b1141SJustin Chen 	{ .compatible = "brcm,brcmstb-timers" },
3550e9b1141SJustin Chen 	{ /* sentinel */ }
3560e9b1141SJustin Chen };
3570e9b1141SJustin Chen 
brcmstb_ioremap_node(struct device_node * dn,int index)3580e9b1141SJustin Chen static inline void __iomem *brcmstb_ioremap_node(struct device_node *dn,
3590e9b1141SJustin Chen 						 int index)
3600e9b1141SJustin Chen {
3610e9b1141SJustin Chen 	return of_io_request_and_map(dn, index, dn->full_name);
3620e9b1141SJustin Chen }
3630e9b1141SJustin Chen 
brcmstb_ioremap_match(const struct of_device_id * matches,int index,const void ** ofdata)3640e9b1141SJustin Chen static void __iomem *brcmstb_ioremap_match(const struct of_device_id *matches,
3650e9b1141SJustin Chen 					   int index, const void **ofdata)
3660e9b1141SJustin Chen {
3670e9b1141SJustin Chen 	struct device_node *dn;
3680e9b1141SJustin Chen 	const struct of_device_id *match;
3690e9b1141SJustin Chen 
3700e9b1141SJustin Chen 	dn = of_find_matching_node_and_match(NULL, matches, &match);
3710e9b1141SJustin Chen 	if (!dn)
3720e9b1141SJustin Chen 		return ERR_PTR(-EINVAL);
3730e9b1141SJustin Chen 
3740e9b1141SJustin Chen 	if (ofdata)
3750e9b1141SJustin Chen 		*ofdata = match->data;
3760e9b1141SJustin Chen 
3770e9b1141SJustin Chen 	return brcmstb_ioremap_node(dn, index);
3780e9b1141SJustin Chen }
3790e9b1141SJustin Chen 
brcmstb_pm_init(void)3800e9b1141SJustin Chen static int brcmstb_pm_init(void)
3810e9b1141SJustin Chen {
3820e9b1141SJustin Chen 	struct device_node *dn;
3830e9b1141SJustin Chen 	void __iomem *base;
3840e9b1141SJustin Chen 	int i;
3850e9b1141SJustin Chen 
3860e9b1141SJustin Chen 	/* AON ctrl registers */
3870e9b1141SJustin Chen 	base = brcmstb_ioremap_match(aon_ctrl_dt_ids, 0, NULL);
3880e9b1141SJustin Chen 	if (IS_ERR(base)) {
3890e9b1141SJustin Chen 		pr_err("error mapping AON_CTRL\n");
3900e9b1141SJustin Chen 		goto aon_err;
3910e9b1141SJustin Chen 	}
3920e9b1141SJustin Chen 	ctrl.aon_ctrl_base = base;
3930e9b1141SJustin Chen 
3940e9b1141SJustin Chen 	/* AON SRAM registers */
3950e9b1141SJustin Chen 	base = brcmstb_ioremap_match(aon_ctrl_dt_ids, 1, NULL);
3960e9b1141SJustin Chen 	if (IS_ERR(base)) {
3970e9b1141SJustin Chen 		pr_err("error mapping AON_SRAM\n");
3980e9b1141SJustin Chen 		goto sram_err;
3990e9b1141SJustin Chen 	}
4000e9b1141SJustin Chen 	ctrl.aon_sram_base = base;
4010e9b1141SJustin Chen 
4020e9b1141SJustin Chen 	ctrl.num_memc = 0;
4030e9b1141SJustin Chen 	/* Map MEMC DDR PHY registers */
4040e9b1141SJustin Chen 	for_each_matching_node(dn, ddr_phy_dt_ids) {
4050e9b1141SJustin Chen 		i = ctrl.num_memc;
4060e9b1141SJustin Chen 		if (i >= MAX_NUM_MEMC) {
4070e9b1141SJustin Chen 			pr_warn("Too many MEMCs (max %d)\n", MAX_NUM_MEMC);
408*f5029f62SWan Jiabing 			of_node_put(dn);
4090e9b1141SJustin Chen 			break;
4100e9b1141SJustin Chen 		}
4110e9b1141SJustin Chen 		base = brcmstb_ioremap_node(dn, 0);
412*f5029f62SWan Jiabing 		if (IS_ERR(base)) {
413*f5029f62SWan Jiabing 			of_node_put(dn);
4140e9b1141SJustin Chen 			goto ddr_err;
415*f5029f62SWan Jiabing 		}
4160e9b1141SJustin Chen 
4170e9b1141SJustin Chen 		ctrl.memcs[i].ddr_phy_base = base;
4180e9b1141SJustin Chen 		ctrl.num_memc++;
4190e9b1141SJustin Chen 	}
4200e9b1141SJustin Chen 
4210e9b1141SJustin Chen 	/* MEMC ARB registers */
4220e9b1141SJustin Chen 	base = brcmstb_ioremap_match(arb_dt_ids, 0, NULL);
4230e9b1141SJustin Chen 	if (IS_ERR(base)) {
4240e9b1141SJustin Chen 		pr_err("error mapping MEMC ARB\n");
4250e9b1141SJustin Chen 		goto ddr_err;
4260e9b1141SJustin Chen 	}
4270e9b1141SJustin Chen 	ctrl.memcs[0].arb_base = base;
4280e9b1141SJustin Chen 
4290e9b1141SJustin Chen 	/* Timer registers */
4300e9b1141SJustin Chen 	base = brcmstb_ioremap_match(timers_ids, 0, NULL);
4310e9b1141SJustin Chen 	if (IS_ERR(base)) {
4320e9b1141SJustin Chen 		pr_err("error mapping timers\n");
4330e9b1141SJustin Chen 		goto tmr_err;
4340e9b1141SJustin Chen 	}
4350e9b1141SJustin Chen 	ctrl.timers_base = base;
4360e9b1141SJustin Chen 
4370e9b1141SJustin Chen 	/* s3 cold boot aka s5 */
4380e9b1141SJustin Chen 	pm_power_off = brcmstb_pm_s5;
4390e9b1141SJustin Chen 
4400e9b1141SJustin Chen 	suspend_set_ops(&brcmstb_pm_ops);
4410e9b1141SJustin Chen 
4420e9b1141SJustin Chen 	return 0;
4430e9b1141SJustin Chen 
4440e9b1141SJustin Chen tmr_err:
4450e9b1141SJustin Chen 	iounmap(ctrl.memcs[0].arb_base);
4460e9b1141SJustin Chen ddr_err:
4470e9b1141SJustin Chen 	for (i = 0; i < ctrl.num_memc; i++)
4480e9b1141SJustin Chen 		iounmap(ctrl.memcs[i].ddr_phy_base);
4490e9b1141SJustin Chen 
4500e9b1141SJustin Chen 	iounmap(ctrl.aon_sram_base);
4510e9b1141SJustin Chen sram_err:
4520e9b1141SJustin Chen 	iounmap(ctrl.aon_ctrl_base);
4530e9b1141SJustin Chen aon_err:
4540e9b1141SJustin Chen 	return PTR_ERR(base);
4550e9b1141SJustin Chen }
4560e9b1141SJustin Chen arch_initcall(brcmstb_pm_init);
457