162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Power Management driver for Marvell Kirkwood SoCs
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2013 Ezequiel Garcia <ezequiel@free-electrons.com>
662306a36Sopenharmony_ci * Copyright (C) 2010 Simon Guinot <sguinot@lacie.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/suspend.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci#include "kirkwood.h"
1362306a36Sopenharmony_ci#include "kirkwood-pm.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic void __iomem *ddr_operation_base;
1662306a36Sopenharmony_cistatic void __iomem *memory_pm_ctrl;
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic void kirkwood_low_power(void)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	u32 mem_pm_ctrl;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	mem_pm_ctrl = readl(memory_pm_ctrl);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	/* Set peripherals to low-power mode */
2562306a36Sopenharmony_ci	writel_relaxed(~0, memory_pm_ctrl);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	/* Set DDR in self-refresh */
2862306a36Sopenharmony_ci	writel_relaxed(0x7, ddr_operation_base);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	/*
3162306a36Sopenharmony_ci	 * Set CPU in wait-for-interrupt state.
3262306a36Sopenharmony_ci	 * This disables the CPU core clocks,
3362306a36Sopenharmony_ci	 * the array clocks, and also the L2 controller.
3462306a36Sopenharmony_ci	 */
3562306a36Sopenharmony_ci	cpu_do_idle();
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	writel_relaxed(mem_pm_ctrl, memory_pm_ctrl);
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic int kirkwood_suspend_enter(suspend_state_t state)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	switch (state) {
4362306a36Sopenharmony_ci	case PM_SUSPEND_STANDBY:
4462306a36Sopenharmony_ci		kirkwood_low_power();
4562306a36Sopenharmony_ci		break;
4662306a36Sopenharmony_ci	default:
4762306a36Sopenharmony_ci		return -EINVAL;
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci	return 0;
5062306a36Sopenharmony_ci}
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic int kirkwood_pm_valid_standby(suspend_state_t state)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	return state == PM_SUSPEND_STANDBY;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic const struct platform_suspend_ops kirkwood_suspend_ops = {
5862306a36Sopenharmony_ci	.enter = kirkwood_suspend_enter,
5962306a36Sopenharmony_ci	.valid = kirkwood_pm_valid_standby,
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_civoid __init kirkwood_pm_init(void)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	ddr_operation_base = ioremap(DDR_OPERATION_BASE, 4);
6562306a36Sopenharmony_ci	memory_pm_ctrl = ioremap(MEMORY_PM_CTRL_PHYS, 4);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	suspend_set_ops(&kirkwood_suspend_ops);
6862306a36Sopenharmony_ci}
69