18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Power Management driver for Marvell Kirkwood SoCs
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Ezequiel Garcia <ezequiel@free-electrons.com>
68c2ecf20Sopenharmony_ci * Copyright (C) 2010 Simon Guinot <sguinot@lacie.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/suspend.h>
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include "kirkwood.h"
138c2ecf20Sopenharmony_ci#include "kirkwood-pm.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic void __iomem *ddr_operation_base;
168c2ecf20Sopenharmony_cistatic void __iomem *memory_pm_ctrl;
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic void kirkwood_low_power(void)
198c2ecf20Sopenharmony_ci{
208c2ecf20Sopenharmony_ci	u32 mem_pm_ctrl;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	mem_pm_ctrl = readl(memory_pm_ctrl);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	/* Set peripherals to low-power mode */
258c2ecf20Sopenharmony_ci	writel_relaxed(~0, memory_pm_ctrl);
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	/* Set DDR in self-refresh */
288c2ecf20Sopenharmony_ci	writel_relaxed(0x7, ddr_operation_base);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	/*
318c2ecf20Sopenharmony_ci	 * Set CPU in wait-for-interrupt state.
328c2ecf20Sopenharmony_ci	 * This disables the CPU core clocks,
338c2ecf20Sopenharmony_ci	 * the array clocks, and also the L2 controller.
348c2ecf20Sopenharmony_ci	 */
358c2ecf20Sopenharmony_ci	cpu_do_idle();
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	writel_relaxed(mem_pm_ctrl, memory_pm_ctrl);
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic int kirkwood_suspend_enter(suspend_state_t state)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	switch (state) {
438c2ecf20Sopenharmony_ci	case PM_SUSPEND_STANDBY:
448c2ecf20Sopenharmony_ci		kirkwood_low_power();
458c2ecf20Sopenharmony_ci		break;
468c2ecf20Sopenharmony_ci	default:
478c2ecf20Sopenharmony_ci		return -EINVAL;
488c2ecf20Sopenharmony_ci	}
498c2ecf20Sopenharmony_ci	return 0;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic int kirkwood_pm_valid_standby(suspend_state_t state)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	return state == PM_SUSPEND_STANDBY;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic const struct platform_suspend_ops kirkwood_suspend_ops = {
588c2ecf20Sopenharmony_ci	.enter = kirkwood_suspend_enter,
598c2ecf20Sopenharmony_ci	.valid = kirkwood_pm_valid_standby,
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_civoid __init kirkwood_pm_init(void)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	ddr_operation_base = ioremap(DDR_OPERATION_BASE, 4);
658c2ecf20Sopenharmony_ci	memory_pm_ctrl = ioremap(MEMORY_PM_CTRL_PHYS, 4);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	suspend_set_ops(&kirkwood_suspend_ops);
688c2ecf20Sopenharmony_ci}
69