diff options
Diffstat (limited to 'arch/arm/mach-socfpga/pm.c')
-rw-r--r-- | arch/arm/mach-socfpga/pm.c | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/arch/arm/mach-socfpga/pm.c b/arch/arm/mach-socfpga/pm.c new file mode 100644 index 000000000000..1ed89fc2b7a8 --- /dev/null +++ b/arch/arm/mach-socfpga/pm.c @@ -0,0 +1,149 @@ +/* + * arch/arm/mach-socfpga/pm.c + * + * Copyright (C) 2014-2015 Altera Corporation. All rights reserved. + * + * with code from pm-imx6.c + * Copyright 2011-2014 Freescale Semiconductor, Inc. + * Copyright 2011 Linaro Ltd. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/bitops.h> +#include <linux/genalloc.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/of_platform.h> +#include <linux/suspend.h> +#include <asm/suspend.h> +#include <asm/fncpy.h> +#include "core.h" + +/* Pointer to function copied to ocram */ +static u32 (*socfpga_sdram_self_refresh_in_ocram)(u32 sdr_base); + +static int socfpga_setup_ocram_self_refresh(void) +{ + struct platform_device *pdev; + phys_addr_t ocram_pbase; + struct device_node *np; + struct gen_pool *ocram_pool; + unsigned long ocram_base; + void __iomem *suspend_ocram_base; + int ret = 0; + + np = of_find_compatible_node(NULL, NULL, "mmio-sram"); + if (!np) { + pr_err("%s: Unable to find mmio-sram in dtb\n", __func__); + return -ENODEV; + } + + pdev = of_find_device_by_node(np); + if (!pdev) { + pr_warn("%s: failed to find ocram device!\n", __func__); + ret = -ENODEV; + goto put_node; + } + + ocram_pool = dev_get_gen_pool(&pdev->dev); + if (!ocram_pool) { + pr_warn("%s: ocram pool unavailable!\n", __func__); + ret = -ENODEV; + goto put_node; + } + + ocram_base = gen_pool_alloc(ocram_pool, socfpga_sdram_self_refresh_sz); + if (!ocram_base) { + pr_warn("%s: unable to alloc ocram!\n", __func__); + ret = -ENOMEM; + goto put_node; + } + + ocram_pbase = gen_pool_virt_to_phys(ocram_pool, ocram_base); + + suspend_ocram_base = __arm_ioremap_exec(ocram_pbase, + socfpga_sdram_self_refresh_sz, + false); + if (!suspend_ocram_base) { + pr_warn("%s: __arm_ioremap_exec failed!\n", __func__); + ret = -ENOMEM; + goto put_node; + } + + /* Copy the code that puts DDR in self refresh to ocram */ + socfpga_sdram_self_refresh_in_ocram = + (void *)fncpy(suspend_ocram_base, + &socfpga_sdram_self_refresh, + socfpga_sdram_self_refresh_sz); + + WARN(!socfpga_sdram_self_refresh_in_ocram, + "could not copy function to ocram"); + if (!socfpga_sdram_self_refresh_in_ocram) + ret = -EFAULT; + +put_node: + of_node_put(np); + + return ret; +} + +static int socfpga_pm_suspend(unsigned long arg) +{ + u32 ret; + + if (!sdr_ctl_base_addr) + return -EFAULT; + + ret = socfpga_sdram_self_refresh_in_ocram((u32)sdr_ctl_base_addr); + + pr_debug("%s self-refresh loops request=%d exit=%d\n", __func__, + ret & 0xffff, (ret >> 16) & 0xffff); + + return 0; +} + +static int socfpga_pm_enter(suspend_state_t state) +{ + switch (state) { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + outer_disable(); + cpu_suspend(0, socfpga_pm_suspend); + outer_resume(); + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct platform_suspend_ops socfpga_pm_ops = { + .valid = suspend_valid_only_mem, + .enter = socfpga_pm_enter, +}; + +static int __init socfpga_pm_init(void) +{ + int ret; + + ret = socfpga_setup_ocram_self_refresh(); + if (ret) + return ret; + + suspend_set_ops(&socfpga_pm_ops); + pr_info("SoCFPGA initialized for DDR self-refresh during suspend.\n"); + + return 0; +} +arch_initcall(socfpga_pm_init); |