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